1

Consider the following code

var dict = new Dictionary<string, object>
{
    { "key 1", "value 1" },
    { "key 2", 123 }
};
var dictJson = JsonConvert.SerializeObject(dict); // yields {"key 1":"value 1","key 2":123}

var keyValuePair = dict.FirstOrDefault();
var keyValuePairJson = JsonConvert.SerializeObject(keyValuePair); // yields {"Key":"key 1","Value":"value 1"}

First question is, why is that the json of the first element of the dictionary, which is a KeyValuePair<TKey, TValue>, different than the json of the Dictionary<TKey, TValue>?

Second question is, how can I achieve a similar json to the serialized dictionary but with only having one item instead of an extended collection? My aim is to have a class that is similar to the below but without getting Key and Value as properties in the serialized json.

public class Foo
{
    public KeyValuePair<string, object> Pair { get; set; }
}
Mohamad Mustafa
  • 65
  • 1
  • 2
  • 8
  • 1
    Why did you expect them to be the same? `KeyValuePair` and `Dictionary` are completely different things. – Sweeper Oct 20 '21 at 19:08
  • @Sweeper Getting the first element of the dictionary, I expected the serialized json to look the same, but only having one element. – Mohamad Mustafa Oct 20 '21 at 19:10
  • 1
    There's probably a built-in serializer for `Dictionary` but `KeyValuePair` reverts to the default `Object` serializer. – IllusiveBrian Oct 20 '21 at 19:12
  • 1
    @IllusiveBrian right track but i believe its the other way round. There is a KeyValuePairConverter that is presumably automatically used for Dictionaries. But when you throw in a KeyValuePair, a uncommon thing to do, its not used. So using the SerializeObject overload where you can state the Converters to use you should yield the same result. – Ralf Oct 20 '21 at 19:23

1 Answers1

1

why is that the json of the first element of the dictionary, which is a KeyValuePair<TKey, TValue>, different than the json of the Dictionary<TKey, TValue>?

Dictionaries and key value pairs are very different things. Dictionaries are not just a collection of key value pairs. That is one view of dictionaries, sure, but it would be very wrong to say that dictionaries are simply that.

More concretely, there is a JsonConverter in NewtonsoftJson that specifically converts KeyValuePair to JSON of the form:

{ "Key": ..., "Value": ... }

KeyValuePairConverter.

Note that this form converts both the key and the value to JSON, which is most likely what you want when you are converting "a key and a value" to JSON. Compare this to what the dictionary converter does when the dictionary's key is not a string - it just calls ToString to make it a string :(

how can I achieve a similar json to the serialized dictionary but with only having one item instead of an extended collection?

You can write a JsonConverter like this:

public class KeyValuePairObjectConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        JToken t = JToken.FromObject(value);

        if (t.Type != JTokenType.Object)
        {
            t.WriteTo(writer);
        }
        else
        {
            JObject o = (JObject)t;
            string key = o.Value<string>("Key");
            var val = o["Value"];
            writer.WriteStartObject();
            writer.WritePropertyName(key);
            val.WriteTo(writer);
            writer.WriteEndObject();
        }
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
        => throw new NotImplementedException();

    public override bool CanRead => false;
    public override bool CanConvert(Type t)
    {
        if (t.IsValueType && t.IsGenericType)
        {
            return (t.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) &&
                    t.GetGenericArguments()[0] == typeof(string);
        }

        return false;
    }
}

Usage:

public class Foo
{
    [JsonConverter(typeof(KeyValuePairObjectConverter))]
    public KeyValuePair<string, object> Pair { get; set; }
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Awesome, exactly what I'm looking for. Only one issue I could find is when I add this converter in `JsonConvert.DefaultSettings` it throws a `StackOverflowException`. It gets stuck on the `JToken t = JToken.FromObject(value);` line. It seems like in this case, the creation of the object would need to be handled manually, it's explained in this answer https://stackoverflow.com/questions/22407321/stackoverflowexception-in-my-jsonconverter-class-when-using-jsonconverter-attr – Mohamad Mustafa Oct 21 '21 at 04:41