Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

json.net: serialise base class members first

Tags:

json

c#

json.net

I'm using json.net to store a serialised object that I would like people to be able to edit in a text editor. I have a base class that contains the name of the object and then a class that inherits from that and adds some other properties.

The problem is that the the properties are written out such that the derived classes properties are written first, and then the base class afetr, so I get:

{
  "MySpecialFiled": 4,
  "Name": "This Is My Object",
  "AnotherBaseField": 8,
}

rather than:

{
  "Name": "This Is My Object",
  "AnotherBaseField": 8,
  "MySpecialFiled": 4,
}

You can see how this get's a bit of a pain when you have a bunch of fields in the derived class and want to actually view/edit in a text editor!

I've messed around with the source code particularly:

public static IEnumerable<FieldInfo> GetFields(Type targetType, BindingFlags bindingAttr)

and

public static IEnumerable<PropertyInfo> GetProperties(Type targetType, BindingFlags bindingAttr)

in ReflectionUtils.cs, to try and reverse the order so that base class properties come first, but I've not had any success yet. Am I missing something trivial?

like image 986
steeveeet Avatar asked Nov 11 '13 14:11

steeveeet


2 Answers

I don't think you need to change JSON.Net's code to do it. Apparently you can do it with a custom contract resolver - by inheriting from DefaultContractResolver - as shown in this code pasted by someone with a similar issue on the json.net forum. That poster overrides CreateProperties and sorts the properties by how deeply inherited the defining type is.

The following code is based on code from that post (by LittleColin on CodePlex). This compiles but isn't tested:

public class CustomPropertySortContractResolver : DefaultContractResolver
{
    private const int MaxPropertiesPerContract = 1000;

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var members = GetSerializableMembers(type);
        if (members == null)
        {
            throw new JsonSerializationException("Null collection of serializable members returned.");
        }

        return members.Select(member => CreateProperty(member, memberSerialization))
                      .Where(x => x != null)
                      .OrderBy(p => (p.Order
                                       + (MaxPropertiesPerContract * GetTypeDepth(p.DeclaringType))) 
                                    ?? -1)
                      .ToList();
    }

    private static int GetTypeDepth(Type type)
    {
        int depth = 0;
        while ((type = type.BaseType) != null)
        {
            depth++;
        }

        return depth;
    }
}

See also this project for similar code that filters the properties to be serialized.

like image 89
sinelaw Avatar answered Oct 17 '22 21:10

sinelaw


Just to complement, I have suggested another approach different than accepted answer using [JsonProperty(Order = -2)] here:

Order of fields when serializing the derived class in JSON.NET

like image 3
Moshtaf Avatar answered Oct 17 '22 19:10

Moshtaf