Nick
Nick

Reputation: 161

Creating json object with duplicate keys in C#

I am creating a json string by serializing an object in C# (using Newtonsoft) to pass to a third party charting library, so have no control over the structure I need to create. The structure requires an object with duplicate keys, something like;

{ "color": "dd3333",
  "linewidth": 2,
  "dataset": [ 3,4,5,6 ],
  "dataset": [ 5,6,7,8]
}

I'm struggling to get the output I want. I was using a dictionary, but have just come across the need for duplicate keys, which scuppers that. I've had a look at creating my own object to support duplicate keys (starting with these suggestions Duplicate keys in .NET dictionaries?) but am still struggling to get them to serialize to the format I need.

Any suggestions would be very welcome!

Upvotes: 1

Views: 2445

Answers (1)

Nick
Nick

Reputation: 161

To solve this, I created a dictionary object that accepted duplicate keys (copied from Duplicate keys in .NET dictionaries?), and added a JsonConverter to control how the object was serialized;

  public class MultiMap<TKey, TValue>
  {
    private readonly Dictionary<TKey, IList<TValue>> storage;

    public MultiMap()
    {
      storage = new Dictionary<TKey, IList<TValue>>();
    }

    public void Add(TKey key, TValue value)
    {
      if (!storage.ContainsKey(key)) storage.Add(key, new List<TValue>());
      storage[key].Add(value);
    }

    public IEnumerable<TKey> Keys
    {
      get { return storage.Keys; }
    }

    public bool ContainsKey(TKey key)
    {
      return storage.ContainsKey(key);
    }

    public IList<TValue> this[TKey key]
    {
      get
      {
        if (!storage.ContainsKey(key))
          throw new KeyNotFoundException(
              string.Format(
                  "The given key {0} was not found in the collection.", key));
        return storage[key];
      }
    }
  }

The JsonConverter object was as follows (I didn't bother with the read as I didn't need it, but it could be easily implemented);

public class MultiMapJsonConverter<TKey, TValue> : JsonConverter
  {
    public override bool CanConvert(Type objectType)
    {
      return objectType == typeof(MultiMap<TKey, TValue>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
      throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

    public override bool CanRead
    {
      get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
      writer.WriteStartObject();
      MultiMap<TKey, TValue> m = (MultiMap<TKey, TValue>)value;

      foreach (TKey key in m.Keys)
      {
        foreach (TValue val in m[key])
        {
          writer.WritePropertyName(key.ToString());
          JToken.FromObject(val).WriteTo(writer);
        }
      }
      writer.WriteEndObject();
    }
  }

With this defined , the following code;

var mm = new MultiMap<string, object>();
mm.Add("color", "dd3333");
mm.Add("linewidth", 2);
mm.Add("dataset", new int[] { 3,4,5,6 });
mm.Add("dataset", new int[] { 5,6,7,8 });
var json = JsonConvert.SerializeObject(mm, new JsonConverter[] { new MultiMapJsonConverter<string, object>() });

gives me the json output;

{"color":"dd3333","linewidth":2,"dataset":[3,4,5,6],"dataset":[5,6,7,8]}

Upvotes: 3

Related Questions