How to deserialize JSON with duplicate property names in the same object

后端 未结 3 1153
长发绾君心
长发绾君心 2020-11-29 12:24

I have a JSON string that I expect to contain duplicate keys that I am unable to make JSON.NET happy with.

I was wondering if anybody knows the best way (maybe usin

相关标签:
3条回答
  • 2020-11-29 12:49

    You should not be using a generic type of object, it should be a more specific type.

    However you json is malformed which is you rmain problem

    You have :

    "{ \"HiThere\":1, \"HiThere\":2, \"HiThere\":3 }"
    

    But it should be:

    "{"HiTheres": [{\"HiThere\":1}, {\"HiThere\":2}, {\"HiThere\":3} ]}"
    

    Or

    "{ \"HiThereOne\":1, \"HiThereTwo\":2, \"HiThereThree\":3 }"
    

    You json is one object with 3 fields with all the same name ("HiThere"). Which wont work.

    The json I have shown gives: An array (HiTheres) of three objects each with a field of HiThere Or One object with three field with different names. (HiThereOne, HiThereTwo, "HiThereThree)

    Have a look at http://jsoneditoronline.org/index.html And http://json.org/

    0 讨论(0)
  • 2020-11-29 12:55

    Interesting question. I played around with this for a while and discovered that while a JObject cannot contain properties with duplicate names, the JsonTextReader used to populate it during deserialization does not have such a restriction. (This makes sense if you think about it: it's a forward-only reader; it is not concerned with what it has read in the past). Armed with this knowledge, I took a shot at writing some code that will populate a hierarchy of JTokens, converting property values to JArrays as necessary if a duplicate property name is encountered in a particular JObject. Since I don't know your actual JSON and requirements, you may need to make some adjustments to it, but it's something to start with at least.

    Here's the code:

    public static JToken DeserializeAndCombineDuplicates(JsonTextReader reader)
    {
        if (reader.TokenType == JsonToken.None)
        {
            reader.Read();
        }
    
        if (reader.TokenType == JsonToken.StartObject)
        {
            reader.Read();
            JObject obj = new JObject();
            while (reader.TokenType != JsonToken.EndObject)
            {
                string propName = (string)reader.Value;
                reader.Read();
                JToken newValue = DeserializeAndCombineDuplicates(reader);
    
                JToken existingValue = obj[propName];
                if (existingValue == null)
                {
                    obj.Add(new JProperty(propName, newValue));
                }
                else if (existingValue.Type == JTokenType.Array)
                {
                    CombineWithArray((JArray)existingValue, newValue);
                }
                else // Convert existing non-array property value to an array
                {
                    JProperty prop = (JProperty)existingValue.Parent;
                    JArray array = new JArray();
                    prop.Value = array;
                    array.Add(existingValue);
                    CombineWithArray(array, newValue);
                }
    
                reader.Read();
            }
            return obj;
        }
    
        if (reader.TokenType == JsonToken.StartArray)
        {
            reader.Read();
            JArray array = new JArray();
            while (reader.TokenType != JsonToken.EndArray)
            {
                array.Add(DeserializeAndCombineDuplicates(reader));
                reader.Read();
            }
            return array;
        }
    
        return new JValue(reader.Value);
    }
    
    private static void CombineWithArray(JArray array, JToken value)
    {
        if (value.Type == JTokenType.Array)
        {
            foreach (JToken child in value.Children())
                array.Add(child);
        }
        else
        {
            array.Add(value);
        }
    }
    

    And here's a demo:

    class Program
    {
        static void Main(string[] args)
        {
            string json = @"
            {
                ""Foo"" : 1,
                ""Foo"" : [2],
                ""Foo"" : [3, 4],
                ""Bar"" : { ""X"" : [ ""A"", ""B"" ] },
                ""Bar"" : { ""X"" : ""C"", ""X"" : ""D"" },
            }";
    
            using (StringReader sr = new StringReader(json))
            using (JsonTextReader reader = new JsonTextReader(sr))
            {
                JToken token = DeserializeAndCombineDuplicates(reader);
                Dump(token, "");
            }
        }
    
        private static void Dump(JToken token, string indent)
        {
            Console.Write(indent);
            if (token == null)
            {
                Console.WriteLine("null");
                return;
            }
            Console.Write(token.Type);
    
            if (token is JProperty)
                Console.Write(" (name=" + ((JProperty)token).Name + ")");
            else if (token is JValue)
                Console.Write(" (value=" + token.ToString() + ")");
    
            Console.WriteLine();
    
            if (token.HasValues)
                foreach (JToken child in token.Children())
                    Dump(child, indent + "  ");
        }
    }
    

    Output:

    Object
      Property (name=Foo)
        Array
          Integer (value=1)
          Integer (value=2)
          Integer (value=3)
          Integer (value=4)
      Property (name=Bar)
        Array
          Object
            Property (name=X)
              Array
                String (value=A)
                String (value=B)
          Object
            Property (name=X)
              Array
                String (value=C)
                String (value=D)
    
    0 讨论(0)
  • 2020-11-29 13:06

    Brian Rogers - Here is the helper function of the JsonConverter that I wrote. I modified it based on your comments about how a JsonTextReader is just a forward-reader doesn't care about duplicate values.

    private static object GetObject(JsonReader reader)
    {
        switch (reader.TokenType)
        {
            case JsonToken.StartObject:
            {
                var dictionary = new Dictionary<string, object>();
    
                while (reader.Read() && (reader.TokenType != JsonToken.EndObject))
                {
                    if (reader.TokenType != JsonToken.PropertyName)
                        throw new InvalidOperationException("Unknown JObject conversion state");
    
                    string propertyName = (string) reader.Value;
    
                    reader.Read();
                    object propertyValue = GetObject(reader);
    
                    object existingValue;
                    if (dictionary.TryGetValue(propertyName, out existingValue))
                    {
                        if (existingValue is List<object>)
                        {
                            var list = existingValue as List<object>;
                            list.Add(propertyValue);
                        }
                        else
                        {
                            var list = new List<object> {existingValue, propertyValue};
                            dictionary[propertyName] = list;
                        }
                    }
                    else
                    {
                        dictionary.Add(propertyName, propertyValue);
                    }
                }
    
                return dictionary;
            }
            case JsonToken.StartArray:
            {
                var list = new List<object>();
    
                while (reader.Read() && (reader.TokenType != JsonToken.EndArray))
                {
                    object propertyValue = GetObject(reader);
                    list.Add(propertyValue);
                }
    
                return list;
            }
            default:
            {
                return reader.Value;
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题