Asked  7 Months ago    Answers:  5   Viewed   33 times

I need to deserialize a complex JSON blob into standard .NET containers for use in code that is not aware of JSON. It expects things to be in standard .NET types, specifically Dictionary<string, object> or List<object> where "object" can be primitive or recurse (Dictionary or List).

I cannot use a static type to map the results and JObject/JToken don't fit. Ideally, there would be some way (via Contracts perhaps?) to convert raw JSON into basic .NET containers.

I've search all over for any way to coax the JSON.NET deserializer into creating these simple types when it encounters "{}" or "[]" but with little success.

Any help appreciated!

 Answers

44

You cannot do what I was asking. At least not as far as I can tell after MUCH research. I had to edit the source of Json.NET.

Tuesday, June 1, 2021
 
KingCrunch
answered 7 Months ago
70
IEnumerable<int> ids = list.Select(x=>x.ID).Distinct();
Wednesday, June 16, 2021
 
laurent
answered 6 Months ago
97

Looks like this is an intended behavior of Json.NET. From ReflectionUtils.cs:

    private static void GetChildPrivateProperties(IList<PropertyInfo> initialProperties, Type targetType, BindingFlags bindingAttr)
    {
        // fix weirdness with private PropertyInfos only being returned for the current Type
        // find base type properties and add them to result

        // also find base properties that have been hidden by subtype properties with the same name

        while ((targetType = targetType.BaseType()) != null)
        {
            foreach (PropertyInfo propertyInfo in targetType.GetProperties(bindingAttr))
            {
                PropertyInfo subTypeProperty = propertyInfo;

                if (!IsPublic(subTypeProperty))
                {
                    // have to test on name rather than reference because instances are different
                    // depending on the type that GetProperties was called on
                    int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name);
                    if (index == -1)
                    {
                        initialProperties.Add(subTypeProperty);
                    }
                    else
                    {
                        PropertyInfo childProperty = initialProperties[index];
                        // don't replace public child with private base
                        if (!IsPublic(childProperty))
                        {
                            // replace nonpublic properties for a child, but gotten from
                            // the parent with the one from the child
                            // the property gotten from the child will have access to private getter/setter
                            initialProperties[index] = subTypeProperty;
                        }

This is where the list of properties for a type is generated, and as you can see, there is code that intentionally prefers identically named properties in the base class to the inherited class.

I don't know why Json.NET does this, you might want to report an issue and ask why. In the meantime, you can use an IContractResolver to prevent this behavior selectively:

[System.AttributeUsage(AttributeTargets.Property)]
public class JsonPreferDerivedPropertyAttribute : System.Attribute
{
}

public class PreferDerivedPropertyContractResolver : DefaultContractResolver
{
    static PropertyInfo GetDerivedPropertyRecursive(Type objectType, Type stopType, PropertyInfo property)
    {
        var parameters = property.GetIndexParameters().Select(info => info.ParameterType).ToArray();
        for (; objectType != null && objectType != stopType; objectType = objectType.BaseType)
        {
            var derivedProperty = objectType.GetProperty(
                property.Name,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, property.PropertyType,
                parameters,
                null);
            if (derivedProperty == null)
                continue;
            if (derivedProperty == property)
                return derivedProperty;  // No override.
            if (derivedProperty.GetCustomAttribute<JsonPreferDerivedPropertyAttribute>() != null)
                return derivedProperty;
        }
        return null;
    }

    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        var list = base.GetSerializableMembers(objectType);

        for (int i = 0; i < list.Count; i++)
        {
            var property = list[i] as PropertyInfo;
            if (property == null)
                continue;
            if (property.DeclaringType != objectType)
            {
                var derivedProperty = GetDerivedPropertyRecursive(objectType, property.DeclaringType, property);
                if (derivedProperty == null || derivedProperty == property)
                    continue;
                if (derivedProperty != property 
                    && (property.GetGetMethod(true) == null || derivedProperty.GetGetMethod(true) != null)
                    && (property.GetSetMethod(true) == null || derivedProperty.GetSetMethod(true) != null))
                {
                    list[i] = derivedProperty;
                }
            }
        }

        return list;
    }
}

I recommend doing this selectively because I don't entirely understand why Json.NET does what it does. The code above only overrides the default behavior for derived class properties with the custom JsonPreferDerivedPropertyAttribute attribute applied.

And then use it like:

public class Base
{
    [JsonProperty]
    private string Type { get { return "Base"; } }
}

public class Inherited : Base
{
    [JsonProperty]
    [JsonPreferDerivedPropertyAttribute]
    private string Type { get { return "Inherited"; } }
}

public class VeryInherited : Inherited
{
    [JsonProperty]
    public string VeryInheritedProperty { get { return "VeryInherited"; } }
}

public static class TestOverride
{
    public static void Test()
    {
        var inherited = new Inherited();
        var json1 = JsonConvert.SerializeObject(inherited, Formatting.Indented, new JsonSerializerSettings() { ContractResolver = new PreferDerivedPropertyContractResolver() });

        var veryInherited = new VeryInherited();
        var json2 = JsonConvert.SerializeObject(veryInherited, Formatting.Indented, new JsonSerializerSettings() { ContractResolver = new PreferDerivedPropertyContractResolver() });

        Debug.WriteLine(json1);
        Debug.WriteLine(json2);
    }
}

And the outputs are:

{
  "Type": "Inherited"
}

and

{
  "VeryInheritedProperty": "VeryInherited",
  "Type": "Inherited"
}
Wednesday, August 25, 2021
 
NIKHIL
answered 4 Months ago
15

It looks like you are trying to deserialize your JSON into a Dictionary(Of String, String). However, clearly the value of mentions is not a String; it is an array of Objects. You could try deserializing into a Dictionary(Of String, Object) instead.

Sunday, October 31, 2021
 
nirvair
answered 1 Month ago
17

If you want to have control over how the layout of items look in a list view, you should make your own custom implementation of ArrayAdapter:

A ListAdapter that manages a ListView backed by an array of arbitrary objects. (...) To use something other than TextViews for the array display, for instance, ImageViews, or to have some of data besides toString() results fill the views, override getView(int, View, ViewGroup) to return the type of view you want.

If you just google for custom arrayadapter example, you should find enough examples showing you how to implement this. Two such examples are:

  • http://www.softwarepassion.com/android-series-custom-listview-items-and-adapters/
  • http://android-er.blogspot.com/2010/06/custom-arrayadapter-with-with-different.html

Good luck :)

Thursday, December 2, 2021
 
mattbilson
answered 4 Days ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share