mardi 2 mars 2021

Deserialize an XML array into a list using reflection

I want to deserialize an XML document into an object, this is my object definition:

public class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<string> Hobbies { get; set; }
}

And this is the XML file, the node names match class properties:

<?xml version="1.0" encoding="UTF-8"?>
<items>
    <item type="dict">
        <FirstName type="str">John</FirstName>
        <LastName type="str">Smith</LastName>
        <Hobbies type="list">
            <item type="str">Reading</item>
            <item type="str">Singing</item>
            <item type="str">Tennis</item>
        </Hobbies>
    </item>
</items>

The following code used to work, I would pass XML node (item in this case) to the function and the code would use reflection to match the property with child nodes and set the property values:

public void DeserializeNode(XmlNode node)
{
    var student = new Student();

    foreach (XmlNode child in node)
    {
        PropertyInfo prop = student.GetType().GetProperty(child.Name);
        prop.SetValue(student, child.InnerText);
    }
}

But the above function does not work anymore (the XML input has changed and now it has an array called hobbies)

The following line throws exception:

prop.SetValue(student, child.InnerText); // child.InnerText = ReadingSingingTennis

This is because child.InnerText for the Hobbies return ReadingSingingTennis and the code tries to set the List<string> tp a single string.

How can modify this function to set the hobbies correctly?





lundi 1 mars 2021

Reflection - SetValue from deep context

I am facing an issue, surely due to my lack of knowledge in the reflection process, while trying to set a "complex" class hierarchy based on Json files.

Here are my main model :

public class Names
{
    public Weapons Weapons { get; set; }
    public Armors Armors { get; set; }
    public Utilities Utilities { get; set; }
    public Names()
    {
        Weapons = new Weapons();
        Armors = new Armors();
        Utilities = new Utilities();
    }
}

Each of them having a list of sub-model like this:

public class Weapons
{
    public BattleAxe BattleAxe { get; set; } = new BattleAxe();
    public Bomb_Missile Bomb_Missile { get; set; } = new Bomb_Missile();
    // etc... Around 20 to 25
}

And finally the ended model which is the exact equivalent of each json files but may have very different properties :

public class BattleAxe
{
    public string[] Normal { get; set; } = new string[0];
    public string[] DescriptiveAdjective { get; set; } = new string[0];
    public string[] Material { get; set; } = new string[0];
    public string[] Type { get; set; } = new string[0];
    public string[] Title { get; set; } = new string[0];
    public string[] Of { get; set; } = new string[0];
    public string[] NormalForTitle { get; set; } = new string[0];
}

Since the MS Json deserializer does not support the conversion to a $type as Newtonsoft before, I tried to populate the values using reflection too like this (I've removed all the null-check for code readability) :

public static void Load()
{
    Names = new Names();
    foreach (var category in Names.GetType().GetProperties())
    {
        if (category is not null && !(category.GetGetMethod()?.IsStatic ?? false))
        {
            var categoryType = category.PropertyType;
            foreach (var item in category.PropertyType.GetProperties())
            {
                var itemType = item.PropertyType;
                var subTypeData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(File.ReadAllText($"./Assets/Names/{categoryType.Name}/{itemType.Name}.json"));
                var concreteObj = Activator.CreateInstance(itemType);
                foreach (var key in subTypeData.Keys)
                {
                    if (itemType.GetProperty(key) is not null && concreteObj is not null)
                    {
                        var prop = concreteObj.GetType().GetProperty(key);
                        var convertedValue = ConvertJsonType(subTypeData[key], subTypeData[key].ValueKind, out var isReferenceType);
                        // It fails here
                        prop.SetValue(
                            isReferenceType ? convertedValue : null,
                            !isReferenceType ? convertedValue : null
                        );
                    }
                }
                item.SetValue(concreteObj, null);
            }
        }
    }
}

So it fails at the prop.SetValue(...) of the deepest object in the hierarchy with a different error depending on the type of value to set. If it is a reference, it throws a System.Reflection.TargetException : 'Object does not match target type' Exception And if it is value, it throw a System.Reflection.TargetException : 'Non-static method requires a target.' Knowing that I do not have problems around the deserialization as shown here, only the fact that I use a dynamic type (and my instinct tells me it is actually the problem...) debug

I do not add the ConvertJsonType(...) body as it is functional and really simple

I am more interested in the 'why' than the 'how' so if you can explain me the 'theory' behind the problem, that would help quite a lot :)

Thank you!

PS: I know I can simplify the things in a more readable/performant way but I must achieve it with reflection for personal learning :) Same for the System.Text.Json namespace, I do not intend to switch back to Newtonsoft for that





C# functions on generic numeric primitives [duplicate]

I routinely want to do generalized functions on "numbers", whether those numbers are ints or doubles or floats or bytes. Just do your arithmetic, adding ints to get ints or duobles to get double, and then return [that]. Unfortunately, C# doesn't seem to have a base "IAddable" interface that all numbers implement. E.g.

public static Val Sum<Val>(this IEnumerable<Val> vals) where Val: IComparable, IFormattable, IConvertible
{
    Val output = default;
    foreach(Val val in vals)
    {
        if(typeof(Val) == typeof(int))
            output = (Val)((int)output + (int)val);
        else if(typeof(Val) == typeof(double))
            output = (Val)((double)output + (double)val);
        else if(typeof(Val) == typeof(byte))
            output = (Val)((byte)output + (byte)val);
        else 
            throw new Exception("Bad type.");
    }
    return output;
}

So, 1) That sucks. I'd rather restrict the function's use than throw an exception when somebody tries Int16 or whatever. 2) It doesn't even compile. "Cannot convert type 'Val' to 'int'."

Is there a "TryCast", like some have "TryParse"? How do I convince C# to just chill and cast what I tell it to cast?





C# InvokeMethod fail after Newtonsoft serialize

I'm invoking a method dynamically in C# by using the Type.InvokeMember method call.
I have 2 methods, one that accepts a string argument, and one that accepts an int argument.
The code works fine when initialized in code, but fails after Newtonsoft.Json serialization.
Debugging doesn't help since the types seem to be correct. Here is the complete code:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace DynamicMethodCall
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Command> commands = new List<Command>()
            {
                new Command() { ClassName="Module1", MethodName="Method1", Parameters= new object[] { "1" } },
                new Command() { ClassName="Module1", MethodName="Method2", Parameters= new object[] { 1 } }
            };

            foreach (var command in commands)
            {
                string result = command.Invoke();
                Console.WriteLine(result);
            }

            File.WriteAllText("commands.json", JsonConvert.SerializeObject(commands));
            commands = JsonConvert.DeserializeObject<List<Command>>(File.ReadAllText("commands.json"));

            foreach (var command in commands)
            {
                string result = command.Invoke();
                Console.WriteLine(result);
            }
            Console.ReadLine();
        }
    }

    public class Command
    {
        public string ClassName { get; set; }
        public string MethodName { get; set; }
        public object[] Parameters { get; set; }

        public string Invoke()
        {
            try
            {
                Type type = Type.GetType("DynamicMethodCall." + ClassName);
                object instance = Activator.CreateInstance(type);
                string response = (string)type.InvokeMember(MethodName,
                    BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance,
                    null, instance, Parameters);
                return response;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    }

    public class Module1
    {
        public string Method1(string x)
        {
            return $"You called Method1 in Module1 with arg {x}.";
        }

        public string Method2(int x)
        {
            return $"You called Method2 in Module1 with arg {x}.";
        }
    }
}


It returns

  • You called Method1 in Module1 with arg 1.
  • You called Method2 in Module1 with arg 1.
  • You called Method1 in Module1 with arg 1.
  • Method 'DynamicMethodCall.Module1.Method2' not found.

Meaning that after Newtonsoft serialize-deserialize, the method with int argument doesn't work. Anybody know what seems to be the problem?





Can't return all classes marked with specific annotation using spring ClassPathScanningCandidateComponentProvider

I have legacy spring(not boot) application and I want to get all classes marked with specific annotation. To acchieve it I use following answer: https://stackoverflow.com/a/1415338/2674303

So I've created following class:

@Component
public class MyVerifier {

    public void verify() {
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);

        scanner.addIncludeFilter(new AnnotationTypeFilter(MyNotification.class));

        Set<BeanDefinition> set =  scanner.findCandidateComponents("base.pack")

    }

In code I have:

@MyNotification
public class MyClass...

@MyNotification
public class MyOtherClass...

But set variable is empty set. Why ?

How to fix it ?





How to get Implemeting class name for Interface in MethodInfo in C# [closed]

I have a methodInfo object and I would like to get the name of the class the implements this method. When I use methodInfo.DeclaringType() I get the Interface name but I want to get the implementing class name. Thanks!





How to initialise a `type` of slice of interface in Golang

I have described a new type of slice of interface and want to initialise its value.

type ISampleData interface {
     ...
}

type Samples []ISampleData

type SampleData struct {  // This struct implements interface `ISampleData`
    ...
}

My method returns Samples type. I am writing unit test cases where I want to initialise a Samples value to compare (reflect.DeepEqual) with the value returned from the method. How to do it?

What I've tried: I initialised a variable with []SampleData values (Samples is a slice of ISampleData, which is implemented by SampleData), but reflection doesn't show them as equals.