-1

I need to create a C# class that matches this json with the exact brackets like this.

{
    "data": {
        "a": "4",
        "b": "2",
        "c": "3",
        "a": "444",
    },
}

System.Collections.Generic.Dictionary<string, string> does it, but it does not allow multiple entries with the same key, so that won't work.

List<T> with a custom data class, string[2], tuples or valueTuples or KeyValuePair<string, string> creates different json where each entry gets "item1", "item1" or different brackets.

How can I deserialize and serialize this exact JSON? It's a customer's "standard" that I must support.

14
  • 5
    It's not valid JSON with the same key being used multiple times. Commented Jun 21, 2021 at 14:01
  • 4
    well, technically JSON does not explicitly forbid duplicate key names. but it doesn't explicitly forbid clogging your own toilet with toilet paper and epoxy. so you could try a List<KeyValuePair<string,string>> - or you could fix your format so it makes a lot more sense. but maybe you could shed a little light on why you want duplicate keys? Commented Jun 21, 2021 at 14:05
  • 1
    @NineBerry That explains, why it is so hard to find useful examples. Thanks! Commented Jun 21, 2021 at 14:24
  • 1
    Does this answer your question? Creating json object with duplicate keys in C#. It may not since that converter makes duplicate keys be adjacent. Commented Jun 21, 2021 at 14:24
  • 4
    Not sure why this got closed as "needs details or clarity", it's perfectly clear (even if not recommended as per RFC 8259). Do those duplicates answer your question? Commented Jun 21, 2021 at 14:40

1 Answer 1

4

While not technically malformed, JSON objects with duplicated property names are not recommended by most recent JSON RFC, RFC 8259:

An object structure is represented as a pair of curly brackets surrounding zero or more name/value pairs (or members)... The names within an object SHOULD be unique.

An object whose names are all unique is interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings. When the names within an object are not unique, the behavior of software that receives such an object is unpredictable. Many implementations report the last name/value pair only. Other implementations report an error or fail to parse the object, and some implementations report all of the name/value pairs, including duplicates.

You may wish to suggest an alternate JSON format to your customer, such as an array of single key-value pair objects.

That being said, if you must serialize and deserialize objects with duplicate properties, Json.NET will not do this out of the box, and you will have to create a custom JsonConverter to do it manually. Since the JSON object's values are all of the same type (here strings), you could bind to a List<KeyValuePair<string, string>>.

First, define the following model:

public class Model
{
    [JsonConverter(typeof(KeyValueListAsObjectConverter<string>))]
    public List<KeyValuePair<string, string>> data { get; } = new ();
}

And the following generic JsonConverter<List<KeyValuePair<string, TValue>>>:

public class KeyValueListAsObjectConverter<TValue> : JsonConverter<List<KeyValuePair<string, TValue>>>
{
    public override List<KeyValuePair<string, TValue>> ReadJson(JsonReader reader, Type objectType, List<KeyValuePair<string, TValue>> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
            return null;
        reader.AssertTokenType(JsonToken.StartObject);
        var list = existingValue ?? (List<KeyValuePair<string, TValue>>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndObject)
        {
            var name = (string)reader.AssertTokenType(JsonToken.PropertyName).Value;
            var value = serializer.Deserialize<TValue>(reader.ReadToContentAndAssert());
            list.Add(new KeyValuePair<string, TValue>(name, value));
        }
        return list;
    }

    public override void WriteJson(JsonWriter writer, List<KeyValuePair<string, TValue>> value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        foreach (var pair in value)
        {
            writer.WritePropertyName(pair.Key);
            serializer.Serialize(writer, pair.Value);
        }
        writer.WriteEndObject();
    }
}

public static partial class JsonExtensions
{
    public static JsonReader AssertTokenType(this JsonReader reader, JsonToken tokenType) => 
        reader.TokenType == tokenType ? reader : throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, tokenType));
    
    public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
        reader.ReadAndAssert().MoveToContentAndAssert();

    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
            reader.ReadAndAssert();
        while (reader.TokenType == JsonToken.Comment) // Skip past comments.
            reader.ReadAndAssert();
        return reader;
    }

    public static JsonReader ReadAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

And you will be able to deserialize and re-serialize the JSON shown in your question.

Demo fiddle here.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for ending this with an answer that helps me and those who come after me with a similar question.

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.