Deserialize JSON to multiple properties

后端 未结 3 2107
刺人心
刺人心 2020-12-06 06:00

I am programming against a third party API which returns JSON data, but the format can be a little strange. Certain properties can either be an object (which contains an Id

3条回答
  •  眼角桃花
    2020-12-06 06:40

    Here is what I would do in this situation.

    • Only have a single property in your parent class for the child object, and make it of type ChildObject
    • Create a custom JsonConverter which can inspect the JSON and either:
      • deserialize the full instance of the child object if the data is present, or
      • create a new instance of the child object and set its ID, leaving all other properties blank. (Or you could do as Jeff Mercado suggested and have the converter load the object from a database based on the ID, if that applies to your situation.)
    • Optionally, put a property on the child object indicating whether it is fully populated. The converter can set this property during deserialization.

    After deserialization, if there was a ChildObject property in the JSON (with either an ID or a full object value), you are guaranteed to have a ChildObject instance and you can get its ID from it; otherwise, if there was no ChildObject property in the JSON, the ChildObject property in the parent class will be null.

    Below is a full working example to demonstrate. In this example, I modified the parent class to include three separate instances of the ChildObject to show the different possibilities in the JSON (string ID only, full object and neither present). They all use the same converter. I also added a Name property and an IsFullyPopulated property to the ChildObject class.

    Here are the DTO classes:

    public abstract class BaseEntity
    {
        public string Id { get; set; }
    }
    
    public class ChildObject : BaseEntity 
    {
        public string Name { get; set; }
        public bool IsFullyPopulated { get; set; }
    }
    
    public class MyObject
    {
        [JsonProperty("ChildObject1")]
        [JsonConverter(typeof(MyCustomObjectConverter))]
        public ChildObject ChildObject1 { get; set; }
    
        [JsonProperty("ChildObject2")]
        [JsonConverter(typeof(MyCustomObjectConverter))]
        public ChildObject ChildObject2 { get; set; }
    
        [JsonProperty("ChildObject3")]
        [JsonConverter(typeof(MyCustomObjectConverter))]
        public ChildObject ChildObject3 { get; set; }
    }
    

    Here is the converter:

    class MyCustomObjectConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(ChildObject));
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken token = JToken.Load(reader);
            ChildObject child = null;
            if (token.Type == JTokenType.String)
            {
                child = new ChildObject();
                child.Id = token.ToString();
                child.IsFullyPopulated = false;
            }
            else if (token.Type == JTokenType.Object)
            {
                child = token.ToObject();
                child.IsFullyPopulated = true;
            }
            else if (token.Type != JTokenType.Null)
            {
                throw new JsonSerializationException("Unexpected token: " + token.Type);
            }
            return child;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }
    }
    

    Here is the test program to demonstrate the operation of the converter:

    class Program
    {
        static void Main(string[] args)
        {
            string json = @"
            {
                ""ChildObject1"": 
                {
                    ""Id"": ""key1"",
                    ""Name"": ""Foo Bar Baz""
                },
                ""ChildObject2"": ""key2""
            }";
    
            MyObject obj = JsonConvert.DeserializeObject(json);
    
            DumpChildObject("ChildObject1", obj.ChildObject1);
            DumpChildObject("ChildObject2", obj.ChildObject2);
            DumpChildObject("ChildObject3", obj.ChildObject3);
        }
    
        static void DumpChildObject(string prop, ChildObject obj)
        {
            Console.WriteLine(prop);
            if (obj != null)
            {
                Console.WriteLine("   Id: " + obj.Id);
                Console.WriteLine("   Name: " + obj.Name);
                Console.WriteLine("   IsFullyPopulated: " + obj.IsFullyPopulated);
            }
            else
            {
                Console.WriteLine("   (null)");
            }
            Console.WriteLine();
        }
    }
    

    And here is the output of the above:

    ChildObject1
       Id: key1
       Name: Foo Bar Baz
       IsFullyPopulated: True
    
    ChildObject2
       Id: key2
       Name:
       IsFullyPopulated: False
    
    ChildObject3
       (null)
    

提交回复
热议问题