Serializing/Deserializing an object that is derived from Java.Lang.Object throws exception (using System.Text.Json) - xamarin.android

In one of my .net7-android project, I am trying to serialize an object using System.Text.Json. My object is derived from Java.Lang.Object. I am not intrested in serializing/deserializing the base class (Java.Lang.Object).
The exception I am getting is "Serialization and deserialization of 'System.Type' instances are not supported.". Anyone has any ideas how can it be fixed?

One of the public properties declared by Java.Lang.Object must (directly or indirectly) return an object of type System.Type, thereby causing the exception. Since you don't want to serialize any of these properties anyway, you could create a custom JsonTypeInfo modifier that excludes all properties declared by Java.Lang.Object.
First, define the following extension methods:
public static class JsonExtensions
{
public static Action<JsonTypeInfo> IgnorePropertiesDeclaredBy(Type declaringType)
=> (Action<JsonTypeInfo>) (typeInfo =>
{
if (typeInfo.Kind != JsonTypeInfoKind.Object || !declaringType.IsAssignableFrom(typeInfo.Type))
return;
foreach (var property in typeInfo.Properties)
if (property.GetDeclaringType() == declaringType)
property.ShouldSerialize = static (obj, value) => false;
});
public static Action<JsonTypeInfo> IgnorePropertiesDeclaredBy<TDeclaringType>() => IgnorePropertiesDeclaredBy(typeof(TDeclaringType));
public static Type? GetDeclaringType(this JsonPropertyInfo property) => (property.AttributeProvider as MemberInfo)?.DeclaringType;
}
And now you can use JsonExtensions.IgnorePropertiesDeclaredBy<Java.Lang.Object>() to omit all properties declared by Java.Lang.Object when serializing instances of derived types like so:
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { JsonExtensions.IgnorePropertiesDeclaredBy<Java.Lang.Object>() },
},
// Add other options as required
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
var json = JsonSerializer.Serialize(myJavaObject, options);
Note that this will only suppress properties declared by some base type. Suppressing properties declared by an interface that the type implements is not implemented.
Demo fiddle using a mockup of Java.Lang.Object here: https://dotnetfiddle.net/8vNQS6.

Related

Generics in static methods in Dart

I want to set up a mixin with standard persistence behavior. The mixin looks like that:
mixin Persistence<T> {
static late final PersistenceBase persistence;
static void add(T object) {
persistence.add(object);
}
static void delete(T object) {
persistence.delete(object);
}
static T elementAt(int index) => persistence.elementAt(index);
static Iterable<T> getAll() => persistence.getAll();
static T get first => persistence.getFirst();
static T get last => persistence.getLast();
static int get length => persistence.getLength();
}
Usage of persistence would look like that:
class A with Persistence {
String var1;
int var2;
}
A.add(A("test", 1));
var length = A.getLength();
A firstElement = A.getFirst();
However Dart doesn't allow using generics with static methods:
Static members can't reference type parameters of the class.
Try removing the reference to the type parameter, or making the member an instance member
The point is that I don't want to create instance of A class just to access persistence functionality for the class.
So far I have done this way:
class PersistenceManager<T> {
late final PersistenceBase persistence;
void add(T object) {
persistence.add(object);
}
void delete(T object) {
persistence.delete(object);
}
T elementAt(int index) => persistence.elementAt(index);
Iterable<T> getAll() => persistence.getAll();
T get first => persistence.getFirst();
T get last => persistence.getLast();
int get length => persistence.getLength();
}
class Asset {
static final persistenceMgr = PersistenceManager<Asset>();
...
}
Asset.persistenceMgr.add(Asset());
That though requires adding mandatory static field for each class that would use persistence.
Is there other more elegant solution?
This is not going to work.
Dart mixin application works by mixing in the instance members of the mixin, and doing nothing to the static members.
You won't be able to do A.elementAt(...) with or without generics, because the A class doesn't have any static elementAt method. The Persistance mixin has one, but you have to call it as Persistance.elementAt. It's not the same namespace as A, and there is no inheritance, or mixin-in, of static members.
If you want a PersistanceBase per class, you do need to declare it yourself, and putting the methods which operate on the base onto the same per-class value seems like the optimal design.
So, no, there is no other more elegant solution. What you have is elegant.

Getting type of another generic type from Dart type parameter

I would like to make a generic class which only accepts Lists as a type parameter. But I also want the type parameter of the List. Something like this:
class MyClass<L extends List<T>> {
T foo() {
// ....
}
}
The problem is that that does not work. T is not found. But this does:
class MyClass<L extends List<T>, T> {
T foo() {
// ....
}
}
My only issue with this is that I have to always pass in the extra parameter T which should be inferred from the List type.
var instance = MyClass<List<int>>();
var instance = MyClass<List<int>, int>(); // Extra int kind of redundant
Is there any workaround to this?
The solution is similar to the one provided in this question (the same problem, but in Java): basically, you can't do that in Dart. What you can do is
create a new subclass:
class MyClass2<T> extends MyClass<List<T>, T> { ... }
or
create a factory method:
class MyClass<L extends List<T>, T> {
static MyClass<List<T>, T> fromList<T>(List<T> list) {
return MyClass(...);
}
}

Grails databinding - How to exclude child property

How can I exclude the child domain property when I use the grailsWebDataBinder?
For example, I have domains:
class Car {
String carPropertyToExclude
Set<Detail> details
static hasMany = [details: Detail]
}
class Detail {
String detailPropertyToExclude
static belongsTo= [car: Car]
}
I want to exclude the detailPropertyToExclude from Detail when I call the bind method of grailsWebDataBinder and give the car instance as a parameter
Code:
List blackList = ["carPropertyToExclude"]
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params), null, blackList)
Note:
Don't suggest the bindable: false or variants when excluded from anywhere. Only need to know is there a way to do it by providing blackList as bind() method parameter.
These variants also not working:
List blackList = ["carPropertyToExclude", "details.detailPropertyToExclude"]
List blackList = ["carPropertyToExclude", [Detail.class : "detailPropertyToExclude"]]
The main question is how to prepare the blackList to exclude also child's property?
blacklist parameter supports only direct object properties
you can use DataBindingListener
import grails.databinding.events.DataBindingListenerAdapter
class BlackListener extends DataBindingListenerAdapter{
List<String> list
//returns false if you want to exclude property from binding
public Boolean beforeBinding(Object obj, String propertyName, Object value, Object errors) {
return !list.contains("${obj?.class.name}.${propertyName}".toString())
}
}
...
List blackList = ["Car.carPropertyToExclude", "Details.detailPropertyToExclude"]
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params),
new BlackListener(list:blackList) )
UPD:
Unfortunately the method above does not work with Collection binding.
The problem that SimpleDataBinder.setPropertyValue(...) method loses listener when processing a list.
Not sure if following workaround is good (potentially context initialization required)
but it's possible to register converter for each black list:
import grails.databinding.SimpleDataBinder
import grails.databinding.SimpleMapDataBindingSource
import grails.databinding.converters.ValueConverter
SimpleDataBinder setBlackList(SimpleDataBinder binder, Map<Class,List<String>> blackLists) {
blackLists.each { Class clazz, List<String> blackList ->
def vc = new ValueConverter(){
boolean canConvert(Object value){
return value instanceof Map
}
Object convert(Object value){
def obj = clazz.newInstance()
binder.bind( obj, new SimpleMapDataBindingSource(value), [], blackList )
return obj
}
Class<?> getTargetType(){ clazz }
}
binder.registerConverter(vc)
}
return binder
}
...
Map blackLists = [
(Car.class) : ["carPropertyToExclude"],
(Detail.class) : ["detailPropertyToExclude"]
]
setBlackList(grailsWebDataBinder,blackLists)
...
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params), null,
blackLists[car.getClass()] )
PS: as alternative possible to set grailsWebDataBinder.conversionService...
In a controller you can exclude props from binding by:
def someAction(){
Car car = new Car()
bindData car, params, [exclude: ['carPropertyToExclude', 'details']]
car.details = params.list('details').collect{
bindData new Detail(), [exclude: ['detailPropertyToExclude']]
}
}
You might also want to use the command-objects to represent your form-data.
I have found one solution based on daggett answer. Maybe in greater versions, the bug is fixed or will be fixed. The bug is that when we give the listener as a parameter of bind method for child domains the listener isn't triggered but when we set it as class level listener works. Grails version 3.2.11.
I have created the BlackListener like this:
public class BlackListListener extends DataBindingListenerAdapter {
private final Map<Class<?>, Collection<String>> blackList;
public BlackListListener(Map<Class<?>, Collection<String>> blackList) {
this.blackList = blackList;
}
public Boolean beforeBinding(Object obj, String propertyName, Object value, Object errors) {
Boolean result = Boolean.TRUE;
Collection<String> list = blackList.get(obj.getClass());
if (CollectionUtils.isNotEmpty(list)) {
result = !list.contains(propertyName);
}
return result;
}
}
Then I make my own grailsWebDatabinder bean as prototype:
<bean id="webDataBinder" class="grails.web.databinding.GrailsWebDataBinder" c:_0-ref="grailsApplication"
scope="prototype"/>
<bean id="carBinder" class="CarDataBinder" c:_0-ref="webDataBinder"/>
and then when I want to use data binder I inject the webDataBinder and init listener:
public CarDataBinder(GrailsWebDataBinder grailsWebDataBinder) {
this.grailsWebDataBinder = grailsWebDataBinder;
DataBindingListener blackListListener = new BlackListListener(
ImmutableMap.of(
Car.class, ImmutableSet.of("carPropertyToExclude"),
Detail.class, ImmutableSet.of("detailPropertyToExclude")
)
);
grailsWebDataBinder.setDataBindingListeners(blackListListener);
}
and then:
void bindData(Car car, Map<?, ?> params) {
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params));
}
If there are better ways you can post.

How can i pass an instance's constructor as a type to a function

I am trying to create a base class for my models but I am struggling with the error The name 'cls' isn't a type so it can't be used as a type argument.. So, how can I pass the object's constructor to the Hive.box method?
import 'package:hive/hive.dart';
class AppModel {
#HiveField(0)
int id;
#HiveField(1)
DateTime createdAt;
#HiveField(2)
DateTime updatedAt;
save() async {
final Type cls = this.runtimeType;
// The name 'cls' isn't a type so it can't be used as a type argument.
final Box box = await Hive.openBox<cls>(cls.toString());
await box.put(this.id, this);
return this;
}
}
#HiveType(typeId: 0)
class UserModel extends AppModel {
#HiveField(3)
String email;
#HiveField(4)
String displayName;
}
void main() {
final UserModel user = UserModel()
..email = 'user#domain.com'
..displayName = 'john doe';
user.save().then(() {
print('saved');
});
}
Dart does not have a way to refer to the dynamic type of this (a "self type").
The way such things are often handled is to have a self-type as type argument, so:
class AppModel<T extends AppModel> {
save() async {
final Box box = await Hive.openBox<T>(T.toString());
await box.put(this.id, this as T);
return this;
}
...
and then ensure that each subclass tells the superclass what type it is:
class UserModel extends AppModel<UserModel> {
...
}
(or, if you expect to subclass UserModel eventually:
class UserModel<T extends UserModel> extends AppModel<T> {
...
}
so that a subclass can still pass its type through).
You are also talking about constructors, and for that there is no easy solution.
Dart's type parameters are types, not classes. You cannot access static members or constructors from a type variable, and there is also no other way to pass a class around.
The only way you can have something call a constructor that it doesn't refer to statically, is to wrap the constructor call in a function and pass that function.
(I can't see how you need the constructor here).

Passing in the type of the declaring class for NLog using Autofac

Following on from this question I would like autofac to inject the type of the declaring object into the constructor of my NLog service, so that it can correctly log which type is logging entries.
My NLogService class looks like this...
public class NLogService : ILogService
{
private readonly Logger _logger;
public NLogService(Type t)
{
var consumerType = t.DeclaringType.FullName;
_logger = LogManager.GetLogger(consumerType);
}
However it fails on app startup because it obviously cannot work out what to inject into the constructor of the NLogService with the following error...
None of the constructors found with
'Public binding flags' on type
'MyProduct.Domain.Services.Logging.NLogService'
can be invoked with the available
services and parameters: Cannot
resolve parameter 'System.Type t' of
constructor 'Void .ctor(System.Type)'.
So, my question is - how do i instruct autofac to inject the type of the calling class?
I tried this...
public NLogService(Type t)
{
var method = MethodBase.GetCurrentMethod();
Type consumingType = method.DeclaringType;
var consumerType = consumingType.FullName;
var consumerType = t.DeclaringType.FullName;
_logger = LogManager.GetLogger(consumerType);
}
But i just end up with MyProduct.Domain.Services.Logging.NLogService
What i want is the type of the class that is doing the actual logging.
i have already tried this suggestion and it didnt work for me either.
Could make your NLogService generic, i.e. NLogService<T> and use Autofac's open generics support?
Then you could do this:
public class NLogService<T> : ILogger<T>
{
private readonly Logger _logger;
public NLogService()
{
_logger = LogManager.GetLogger(typeof(T).FullName);
}
}
There is no real good way to do this with Autofac, because does not have support for 'context based injection' (which is what you are trying to do). There is a workaround, but it aint pretty...
What you can do is revert to property injection and define a base class or interface for that ILogService property. For instance, you can define the following interface:
public interface ILoggerContainer
{
public ILogService Logger { get; set; }
}
Now you can implement this interface on all types that need a logger:
public class Consumer : IConsumer, ILoggerContainer
{
public ILogService Logger { get; set; }
}
With this in place you can configure Autofac as follows:
builder.RegisterType<ILoggerContainer>()
.OnActivating(e =>
{
var type = typeof(LogService<>)
.MakeGenericType(e.Instance.GetType());
e.Instance.Logger = e.Context.Resolve(type);
});
Another workaround, that you may find cleaner is to inject an ILogger<T> with the same type as the type of the parent type:
public class Consumer : IConsumer
{
public Consumer(ILogger<Consumer> logger) { }
}
This makes the configuration much easier and prevents you from having to have a base class. Which one is most appropriate is up to you.
As I said, these are workarounds, but to be honest, you might need to reconsider your logging strategy in your application. Perhaps you are logging at too many places. In the applications I write there is hardly ever a need to log, and when I do, I write an logging message that is expressive enough so that there is no need to communicate the type that triggered the event. And when you log exception, you will always have a complete stack trace (and exception logging should almost only happen in the outer layer of your application and not within services anyway).
The following technique works well in our experience:
Create an attribute like below, which can be applied at class level or at the injection site:
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class)]
public class LoggerAttribute : Attribute
{
public readonly string Name;
public LoggerAttribute(string name)
{
Name = name;
}
}
Create an Autofac module that you register with the ContainerBuilder:
public class LogInjectionModule : Module
{
protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration)
{
registration.Preparing += OnComponentPreparing;
}
static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
var typePreparing = e.Component.Activator.LimitType;
// By default, the name supplied to the logging instance is the name of the type in which it is being injected into.
string loggerName = typePreparing.FullName;
//If there is a class-level logger attribute, then promote its supplied name value instead as the logger name to use.
var loggerAttribute = (LoggerAttribute)typePreparing.GetCustomAttributes(typeof(LoggerAttribute), true).FirstOrDefault();
if (loggerAttribute != null)
{
loggerName = loggerAttribute.Name;
}
e.Parameters = e.Parameters.Union(new Parameter[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof (Logger),
(p, i) =>
{
// If the parameter being injected has its own logger attribute, then promote its name value instead as the logger name to use.
loggerAttribute = (LoggerAttribute)
p.GetCustomAttributes(typeof(LoggerAttribute),true).FirstOrDefault();
if (loggerAttribute != null)
{
loggerName = loggerAttribute.Name;
}
// Return a new Logger instance for injection, parameterised with the most appropriate name which we have determined above.
return LogManager.GetLogger(loggerName);
}),
// Always make an unamed instance of Logger available for use in delegate-based registration e.g.: Register((c,p) => new Foo(p.TypedAs<Logger>())
new TypedParameter(typeof(Logger), LogManager.GetLogger(loggerName))
});
}
}
You can now inject a named Logger in any one of these ways depending on individual scenarios:
By default, the injected logger name will be given the full type name of the class it is injected into:
public class Foo
{
public Foo(Logger logger)
{
}
}
Use a constructor parameter [Logger] attribute to override the logger name:
public class Foo
{
public Foo([Logger("Meaningful Name")]Logger logger)
{
}
}
Use a class-level [Logger] attribute to set the same logger name override for all constructor overloads:
[Logger("Meaningful Name")]
public class Foo
{
public Foo(Logger logger, int something)
{
}
public Foo(Logger logger, int something, DateTime somethingElse)
{
}
}
Use constructor parameter [Logger] attributes on each constructor overload to set different logger names depending on the context of how you were constructed:
public class Foo
{
public Foo(Logger("Meaningful Name")]Logger logger, int something)
{
}
public Foo(Logger("Different Name")]Logger logger, int something, DateTime somethingElse)
{
}
}
IMPORTANT NOTE: If you register types to be resolved with logger constructor injection using Autofac's delegate registration, you MUST use the two parameter overload like so: Register((c,p) => new Foo(p.TypedAs<Logger>()).
Hope this helps!
It is possible to do this without generics.
However, please note that in Autofac 6.x, the resolution process has changed to use a resolve pipeline. This doesn't matter for most scenarios, but it does when you want to use the lifetime events like OnPreparing, etc. Most of the answers here on SO around overriding the Preparing event are very old and are now outdated. You can't override Preparing directly anymore.
There is an example on the Autofac documentation site doing this for log4net, and it works with NLog with only minor changes. Here is the basic idea:
public class Log4NetMiddleware : IResolveMiddleware
{
public PipelinePhase Phase => PipelinePhase.ParameterSelection;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
// Add our parameters.
context.ChangeParameters(context.Parameters.Union(
new[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(ILog),
(p, i) => LogManager.GetLogger(p.Member.DeclaringType)
),
}));
// Continue the resolve.
next(context);
// Has an instance been activated?
if (context.NewInstanceActivated)
{
var instanceType = context.Instance.GetType();
// Get all the injectable properties to set.
// If you wanted to ensure the properties were only UNSET properties,
// here's where you'd do it.
var properties = instanceType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);
// Set the properties located.
foreach (var propToSet in properties)
{
propToSet.SetValue(context.Instance, LogManager.GetLogger(instanceType), null);
}
}
}
}
Please also note that you have to understand how middleware works in Autofac. The documentation is a good place to start.

Resources