C# data contract for complex JSON object

后端 未结 3 1613
暗喜
暗喜 2021-01-21 11:32

This is probably something really simple and I looked everywhere and tried everything I could come up with. So I apologize if this is a simple search and I was just looking for

3条回答
  •  囚心锁ツ
    2021-01-21 12:00

    Under normal circumstances, you could define your data property as a List>, like so:

        [DataMember(Name = "data", IsRequired = true)]
        public List> data { get; set; }
    

    Then you would be able to serialize and deserialize it successfully with Json.NET. Unfortunately, one of your data objects has duplicated keys:

      {
         "groupId":"group1",
         "failbackAction":"null",
         "normal":"null",
         "failoverAction":"null",
         "failbackAction":"null",
         "failoverAction":"null",
         "artifactId":"mywebserver",
         "normalState":"null"
      },
    

    Using duplicated keys is not recommended by the JSON standard, which states:

    When the names within an object are not unique, the behavior of software that receives such an object is unpredictable.

    In addition, c# dictionaries of course do not support duplicated keys, and data contract serialization does not duplicated property names.

    However, it is possible to read a JSON object with duplicated keys using Json.NET's JsonReader and create a custom JsonConverter to handle duplicated keys.

    First, define the following class to replace JsonValue. JsonValue is a silverlight-specific class whose use has been deprecated in overall .Net:

    [JsonConverter(typeof(JsonValueListConverter))]
    public sealed class JsonValueList
    {
        public JsonValueList()
        {
            this.Values = new List>();
        }
    
        public List> Values { get; private set; }
    }
    
    class JsonValueListConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(JsonValueList).IsAssignableFrom(objectType);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
            var jsonValue = (existingValue as JsonValueList ?? new JsonValueList());
            if (reader.TokenType != JsonToken.StartObject)
                throw new JsonSerializationException("Invalid reader.TokenType " + reader.TokenType);
            while (reader.Read())
            {
                switch (reader.TokenType)
                {
                    case JsonToken.Comment:
                        break;
                    case JsonToken.PropertyName:
                        {
                            var key = reader.Value.ToString();
                            if (!reader.Read())
                                throw new JsonSerializationException(string.Format("Missing value at path: {0}", reader.Path));
                            var value = serializer.Deserialize(reader);
                            jsonValue.Values.Add(new KeyValuePair(key, value));
                        }
                        break;
                    case JsonToken.EndObject:
                        return jsonValue;
                    default:
                        throw new JsonSerializationException(string.Format("Unknown token {0} at path: {1} ", reader.TokenType, reader.Path));
                }
            }
            throw new JsonSerializationException(string.Format("Unclosed object at path: {0}", reader.Path));
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var jsonValue = (JsonValueList)value;
            writer.WriteStartObject();
            foreach (var pair in jsonValue.Values)
            {
                writer.WritePropertyName(pair.Key);
                writer.WriteValue(pair.Value);
            }
            writer.WriteEndObject();
        }
    }
    

    Notice the use of [JsonConverter(typeof(JsonValueListConverter))]. This specifies the use of a custom converter when serializing and deserializing JsonValueList.

    Next, define your Update_DB class as follows:

    [DataContract]
    public class Update_DB
    {
        [DataMember(Name = "appname", IsRequired = true)]
        public string appname { get; set; }
        [DataMember]
        public string key { get; set; }
    
        [DataMember(Name = "data", IsRequired = true)]
        public List data { get; set; }
    
        [DataMember]
        public string updateId { get; set; }
        [DataMember]
        public string updateTS { get; set; }
        [DataMember]
        public string creationUser { get; set; }
    }
    

    Now you will be able to serialize and deserialize your JSON successfully. Sample fiddle.

    Update

    If you do not have duplicated keys, you can define your class as follows:

    [DataContract]
    public class Update_DB
    {
        [DataMember(Name = "appname", IsRequired = true)]
        public string appname { get; set; }
        [DataMember]
        public string key { get; set; }
    
        [DataMember(Name = "data", IsRequired = true)]
        public List> data { get; set; }
    
        [DataMember]
        public string updateId { get; set; }
        [DataMember]
        public string updateTS { get; set; }
        [DataMember]
        public string creationUser { get; set; }
    }
    

    And then the following:

    var collection = new Update_DB
    {
        data = new List>
        {
            new Dictionary
            {
                {"data1", "10551296"},
                {"data2", "TrainingIns"},
                {"data3", "Completed"},
            },
            new Dictionary
            {
                {"connectorType", "webserver-to-appserver"},
                {"sourceUri", "data4"},
                {"destinationUri", "data5"},
            },
        },
    };
    
    string x = JsonConvert.SerializeObject(collection.data, Formatting.Indented);
    
    Console.WriteLine(x);
    

    Produces the output:

    [
      {
        "data1": "10551296",
        "data2": "TrainingIns",
        "data3": "Completed"
      },
      {
        "connectorType": "webserver-to-appserver",
        "sourceUri": "data4",
        "destinationUri": "data5"
      }
    ]
    

    Sample fiddle.

提交回复
热议问题