1

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!

6
  • 4
    That would be invalid JSON. The major JSON serializers won't produce it. Keys must be unique. If you can, redefine it to use an array of arrays: "datasets": [[ 3,4,5,6 ], [ 5,6,7,8 ]] Also, strict JSON compliance requires quoted key names. Commented Sep 22, 2020 at 17:04
  • I'd like to recommend against this. According to RFC 8259: The names within an object SHOULD be unique.. If you really need this, you will need to create your own custom JsonConverter for the containing class. An example of custom parsing (not serialization) of duplicate keys is here: How to deserialize JSON with duplicate property names in the same object. Commented Sep 22, 2020 at 17:44
  • Thanks for the comment on the names not being in quotes, that was just my mistake, they should have been. I've edited the question. As I said above, I have no choice in providing this structure, I am providing data to a third party app and that's the defined structure. I agree it's not ideal, but hey ho, I'm stuck with it. As to whether it's valid JSON, that seems up for debate. A good discussion on this here stackoverflow.com/questions/21832701/… Commented Sep 22, 2020 at 18:43
  • Thanks for the comments on deserializing, I'd already seen that. Unfortunately I want to serialize the object, the third party app de-serializes, so not my problem! Commented Sep 22, 2020 at 18:45
  • @Nick that discussion (Feb 2014) is from before RFC 8259 (Dec 2017) stated that "the names within an object SHOULD be unique." What library are you using? Requiring something that violates an explicitly stated standard is bad design. Commented Sep 22, 2020 at 18:45

1 Answer 1

3

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]}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.