1

I have a base and derived class and I want to customize the JSON serialization/deserialization of the derived class but build on what would be serialization/deserialization in the base class. I can't see how to do this with a JsonConverter class.

Example Classes

    public class MyBase
    {
        public long id { get; set; }
        public virtual string name { get; set; }
        public string desc { get; set; }
        ...
    }


    [JsonConverter(typeof(MyDerivedSerializer))]
    public class MyDerived : myBase
    {
        public double rate { get; set; }
        public double temp { get; set; }
        ...
    }

    public class MyDerivedSerializer : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
          var baseS = value as MyBase;
          //??? How to get JSON for base class

          //Add items for derived class
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            while ((reader.TokenType != JsonToken.Null) && (reader.TokenType != JsonToken.EndObject))
            {
              JObject jo = JObject.Load(reader);
              JToken itemProps = jo["properties"];
              jo.Remove("properties");
              foreach (JToken c in itemProps.Children())
              {
                if (c.Type == JTokenType.Property)
                {
                  JProperty p = c as JProperty;
                  if (!jo.ContainsKey(p.Name))
                    jo[p.Name] = p.Value;
                }
              }
            }
            //now that the JSON is good, how do I put the data in a created derived object
            var x = Activator.CreateInstance(objectType);
            return x;             
        }

        public override bool CanConvert(Type objectType)
        {
          return true;
        }
    }

The tool using the JSON needs all the derived class properties in a sub field called "properties" but I can't change my derived classes. So I would like to just edit the JSON using the JsonConveter to look like this.

    {
        "id": 33,
        "name": "xSource",
        "desc": "Main source",
        ...
        "properties": {
            "rate": "3.21",
            "temp": "245.2"
            ...
        }

    }
runfastman
  • 927
  • 2
  • 11
  • 31

1 Answers1

1

You will want to clean up a bit but I got this to spit out

{"id":1,"name":"Name","desc":"Desc","properties":{"rate":2.0,"temp":3.0}}

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JToken t = JToken.FromObject((MyBase)value);
        JObject jo = (JObject)t;

        var baseProperties = 
      typeof(MyBase).GetProperties(System.Reflection.BindingFlags.Public
       | System.Reflection.BindingFlags.Instance
       | System.Reflection.BindingFlags.DeclaredOnly);

        var derivedProperties = 
 typeof(MyDerived).GetProperties(System.Reflection.BindingFlags.Public
 | System.Reflection.BindingFlags.Instance
 | System.Reflection.BindingFlags.DeclaredOnly);

      var derivedValues =   jo.Properties().Where(x => derivedProperties.Any(y => y.Name == x.Name));
        var baseValues = jo.Properties().Where(x => baseProperties.Any(y => y.Name == x.Name)).ToList();
        JObject o = new JObject(baseValues);
        o.Add(new JProperty("properties", new JObject(derivedValues)));

        o.WriteTo(writer);

    }

Did have to remove the decorator from the class and move to the deseriliazation call like so was getting stackoverflow.

var result = JsonConvert.SerializeObject(test, new KeysJsonConverter());
Ryan Schlueter
  • 2,223
  • 2
  • 13
  • 19
  • Yea, when I was trying it I got an infinite recursive loop using the decorator. Is there a way to get a JObject from this instead of a string from JsonConvert.SerializeObject. I was using "JObject.FromObject(derivedObj);" before. – runfastman Feb 18 '19 at 22:01
  • Sorry, never mind, I didn't realize FromObject has a formatting option as well. – runfastman Feb 18 '19 at 22:05
  • No problem. Its a pretty rough poc if you need any help getting it up all the way let me know. – Ryan Schlueter Feb 18 '19 at 22:18
  • So is there an easy way to find and remove items (by name?) from the Derived Values. Not sure what it is exactly. – runfastman Feb 18 '19 at 22:25
  • Yep. var excludeList = new List { "rate" }; var derivedValues = jo.Properties().Where(x => derivedProperties.Any(y => y.Name == x.Name) && !excludeList.Any(y => y == x.Name)); include list is just removing the ! – Ryan Schlueter Feb 18 '19 at 22:28
  • So what its doing is using reflection to get the base properties/Derived properties. Then selecting from the JObject just those properties. Then returning a new JObject that we made to our specifications. – Ryan Schlueter Feb 18 '19 at 22:32
  • I though I could figure out a nice way to deserialize, but I failed there as well. Do you have some slick LINQ that can pull the data for the base and derived from the reader? – runfastman Feb 19 '19 at 20:10
  • Could you edit this one with the code you got or create a new one and I will see if I got any thoughts. – Ryan Schlueter Feb 19 '19 at 20:32
  • I edited my code. I am sure there is some nice LINQ way to move the properties into the main JObject, but my biggest issues is to create the derived class with the new JObject data. – runfastman Feb 19 '19 at 22:20