【问题标题】:How to serialize a Dictionary as part of its parent object using Json.Net如何使用 Json.Net 将字典序列化为其父对象的一部分
【发布时间】:2013-02-15 11:23:39
【问题描述】:

我正在使用 Json.Net 进行序列化。 我有一堂带字典的课:

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

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

我能否以某种方式序列化此对象以获得以下 JSON

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

其中“key1”、“key2”是字典中的键?

【问题讨论】:

    标签: c# serialization json.net


    【解决方案1】:

    如果您使用Json.Net 5.0.5 或更高版本,并且愿意将字典类型从Dictionary&lt;string, string&gt; 更改为Dictionary&lt;string, object&gt;,那么完成您想要的一种简单方法是添加[JsonExtensionData]属性到您的字典属性,如下所示:

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

    标记字典的键和值将被序列化为父对象的一部分。好处是它也适用于反序列化:JSON 中与类成员不匹配的任何属性都将被放入字典中。

    【讨论】:

    • 只是简单介绍一下:为什么是Dictionary&lt;string, object&gt;Dictionary&lt;string, JToken&gt;?我想用Dictionary&lt;string, string&gt;,所以一个非常简单的类型但是我得到一个错误
    • @ChristopheGigax 因为“JsonExtensionData”功能被设计为对目标类中没有相应属性的 JSON 中的任何内容的包罗万象。 “任何东西”可能意味着数字、布尔值、数组或子对象,它们都不会进入Dictionary&lt;string, string&gt;。所以你必须使用Dictionary&lt;string, object&gt;Dictionary&lt;string, JToken&gt;,即使你的JSON 恰好只包含字符串值。这就是它的工作方式。如果您想要其他内容,则需要使用自定义 JsonConverter
    • 这对我不起作用。我得到的结果看起来像这样 {[variables, [ { "var": "x", "value": "W" }, { "var": "y", "value": "2" } ]]}我的属性看起来像这样 [JsonProperty("variables")] [JsonExtensionData] public IDictionary Variables = new Dictionary();
    • 当你想从序列化中删除JsonExtensionData属性时你会怎么做?可以将其设置为null,但这似乎很老套。
    • @RoadRunner 将其设置为 null 并非不合理,但我猜这取决于您的要求。我建议您打开new question,以便我们知道您要做什么后进一步探索。
    【解决方案2】:

    实现JsonConverter-派生类:CustomCreationConverter 类应该用作创建自定义对象的基类。

    转换器的草稿版本(可以根据需要改进错误处理):

    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
    }
    

    客户端代码:

    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);
    

    请注意:属性名和字典的键名之间可能存在冲突。

    【讨论】:

    • 谢谢。只有一个补充:我认为最好从 JsonContract (JsonObjectContract)serializer.ContractResolver.ResolveContract(typeof(Test)) 获取属性。使用这种方式可以获取 JsonPropertyAttribute 值。
    【解决方案3】:

    您可以创建此转换器,然后将其分配给您的资源。采取了一些建议的解决方案。

    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));
            }
        }
    

    然后在你的 poco 类中使用它。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-15
      • 1970-01-01
      相关资源
      最近更新 更多