22

I used a Dictionary in a Web API project, which is serializing like that in JSON:

{"keyname":{objectvalue},"keyname2:"....

Since I have duplicate keys I could't use Dictionary type any more, and instead now I'm using List<KeyValuePair<string,object>>.

But this is serializing that way:

[{"Key":"keyname","Value":"objectvalue"}...

Is there a way to have the List<KeyValuePair> serialize the same way a dictionary does?

Thanks.

dana
  • 17,267
  • 6
  • 64
  • 88
user250773
  • 569
  • 1
  • 8
  • 21

2 Answers2

34

If you use the Newtonsoft Json.NET library you can do the following.

Define a converter to write the list of key/value pairs the way you want:

class MyConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        List<KeyValuePair<string, object>> list = value as List<KeyValuePair<string, object>>;
        writer.WriteStartArray();
        foreach (var item in list)
        {
            writer.WriteStartObject();
            writer.WritePropertyName(item.Key);
            writer.WriteValue(item.Value);
            writer.WriteEndObject();
        }
        writer.WriteEndArray();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // TODO...
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(List<KeyValuePair<string, object>>);
    }
}

Then use the converter:

var keyValuePairs = new List<KeyValuePair<string, object>>
                    {
                        new KeyValuePair<string, object>("one", 1),
                        new KeyValuePair<string, object>("two", 2),
                        new KeyValuePair<string, object>("three", 3)
                    };

JsonSerializerSettings settings = new JsonSerializerSettings { Converters = new [] {new MyConverter()} };
string json = JsonConvert.SerializeObject(keyValuePairs, settings);

This generates [{"one":1},{"two":2},{"three":3}]

Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
  • Is there an easy sample how ReadJson method works, too? I have a similar problem while changing Deserialization of Dictionary to List> using this format: {"key1":"test", "key1":"12345678", "key2":"ABC", } – user250773 Jan 09 '14 at 16:26
  • @user250773 [This blog](http://dotnetbyexample.blogspot.com/2012/02/json-deserialization-with-jsonnet-class.html) should be enough info to get you there. – Patrick Quirk Jan 09 '14 at 16:42
  • for information if you want work with `IEnumerable<...>` you can use in method : CanConvert(Type type){ `return GetType(objectType) == typeof(KeyValuePair);` } where GetType `static Type GetType(Type type) { foreach (Type intType in type.GetInterfaces()) { if (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { return intType.GetGenericArguments()[0]; } } return null; }` voilà – maliness Apr 20 '16 at 09:27
  • 3
    What should ReadJson be overriden like? – Euridice01 Sep 19 '16 at 19:43
0

I would modify the accepted answer a little bit

public class MyConverter : JsonConverter<List<KeyValuePair<string, object>>>
{
    public override void WriteJson(JsonWriter writer, List<KeyValuePair<string, object>> value, JsonSerializer serializer)
    {
        List<KeyValuePair<string, object>> list = new(value);

        var keys = list.Select(x => x.Key).Distinct();

        writer.WriteStartObject();

        foreach (var key in keys) {
            var values = list.Where(item => item.Key == key).ToList();
            writer.WritePropertyName(values[0].Key);

            if (values.Count() == 1)
            {                    
                writer.WriteValue(values[0].Value);
            }
            else
            {
                writer.WriteStartArray();
                writer.WriteValue(string.Join(", ", values.Select(v => v.Value)));
                writer.WriteEndArray();
            }               
        }
        writer.WriteEndObject();

    }    

    public override List<KeyValuePair<string, object>>? ReadJson(JsonReader reader, Type objectType, List<KeyValuePair<string, object>>? existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}
Tiju John
  • 933
  • 11
  • 28