简体   繁体   中英

How to serialize a Dictionary as part of its parent object using Json.Net

I'm using Json.Net for serialization. I have a class with a Dictionary:

public class Test
{
    public string X { get; set; }

    public Dictionary<string, string> Y { get; set; }
}

Can I somehow serialize this object to get the following JSON

{
    "X" : "value",
    "key1": "value1",
    "key2": "value2"
}

where "key1", "key2" are keys in the Dictionary?

If you're using Json.Net 5.0.5 or later and you're willing to change the type of your dictionary from Dictionary<string, string> to Dictionary<string, object> , then one easy way to accomplish what you want is to add the [JsonExtensionData] attribute to your dictionary property like this:

public class Test
{
    public string X { get; set; }

    [JsonExtensionData]
    public Dictionary<string, object> Y { get; set; }
}

The keys and values of the marked dictionary will then be serialized as part of the parent object. The bonus is that it works on deserialization as well: any properties in the JSON that do not match to members of the class will be placed into the dictionary.

Implement JsonConverter -derived class: the CustomCreationConverter class should be used as base class to create a custom object .

Draft version of the converter (error handling can be improved as you wish):

internal class TestObjectConverter : CustomCreationConverter<Test>
{
    #region Overrides of CustomCreationConverter<Test>

    public override Test Create(Type objectType)
    {
        return new Test
            {
                Y = new Dictionary<string, string>()
            };
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteStartObject();

        // Write properties.
        var propertyInfos = value.GetType().GetProperties();
        foreach (var propertyInfo in propertyInfos)
        {
            // Skip the Y property.
            if (propertyInfo.Name == "Y")
                continue;

            writer.WritePropertyName(propertyInfo.Name);
            var propertyValue = propertyInfo.GetValue(value);
            serializer.Serialize(writer, propertyValue);
        }

        // Write dictionary key-value pairs.
        var test = (Test)value;
        foreach (var kvp in test.Y)
        {
            writer.WritePropertyName(kvp.Key);
            serializer.Serialize(writer, kvp.Value);
        }
        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jsonObject = JObject.Load(reader);
        var jsonProperties = jsonObject.Properties().ToList();
        var outputObject = Create(objectType);

        // Property name => property info dictionary (for fast lookup).
        var propertyNames = objectType.GetProperties().ToDictionary(pi => pi.Name, pi => pi);
        foreach (var jsonProperty in jsonProperties)
        {
            // If such property exists - use it.
            PropertyInfo targetProperty;
            if (propertyNames.TryGetValue(jsonProperty.Name, out targetProperty))
            {
                var propertyValue = jsonProperty.Value.ToObject(targetProperty.PropertyType);
                targetProperty.SetValue(outputObject, propertyValue, null);
            }
            else
            {
                // Otherwise - use the dictionary.
                outputObject.Y.Add(jsonProperty.Name, jsonProperty.Value.ToObject<string>());
            }
        }

        return outputObject;
    }

    public override bool CanWrite
    {
        get { return true; }
    }

    #endregion
}

Client code:

var test = new Test
    {
        X = "123",
        Y = new Dictionary<string, string>
            {
                { "key1", "value1" },
                { "key2", "value2" },
                { "key3", "value3" },
            }
    };

string json = JsonConvert.SerializeObject(test, Formatting.Indented, new TestObjectConverter());
var deserializedObject = JsonConvert.DeserializeObject<Test>(json);

Please note: there is a potential collision between property names and key names of the dictionary.

You can create this converter and then assign it to your property. Took bits and pieces of of proposed solutions.

public class DictionaryToJsonObjectConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(IDictionary<string, string>).IsAssignableFrom(objectType);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteRawValue(JsonConvert.SerializeObject(value, Formatting.Indented));
        }
    }

Then use it in your poco class.

public class Poco
{
        [JsonProperty("myid")]
        public string Id{ get; set; }

        [JsonProperty("properties")]
        [JsonConverter(typeof(DictionaryToJsonObjectConverter))]
        public IDictionary<string, string> Properties { get; set; }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM