c# - 使用自定义 JsonConverter 来改变对象部分的序列化

标签 c# json.net

我很难重写自定义 JsonConverter 的 WriteJson 方法,以便稍微改变执行序列化的方式。

我需要调用一个 REST 服务来接受具有通用部分的特定输入。我可以使用以下有效负载格式重现我遇到的问题:

public sealed class JsonField
{
    public string Key { get; set; }
    public object Value { get; set; }
    public string OtherParam { get; set; }
}
public sealed class JsonPayload
{
    public string Item1 { get; set; }
    public string Item2 { get; set; }
    public List<JsonField> Fields { get; set; }
}

我正在调用的 REST API 需要让 Fields 成为一个对象,其中包含尽可能多的字段,这些字段的名称与原始集合中指定的 Key 属性相对应。像这样:

{
"Item1" : "Value1",
"Item2" : "Value2",
...
"Fields":
{
    "Key1": {"Value":"Value1", "OtherParam":"other1"}
    "Key2": {"Value":42, "OtherParam":"other2"}
}
}

但是,使用默认选项,有效载荷是这样序列化的:

{
"Item1" : "Value1",
"Item2" : "Value2",
...
"Fields":[
    { "Key":"Key1", "Value":"Value1", "OtherParam":"other1" }
    { "Key":"Key2", "Value":42, "OtherParam":"other2" }
]
}

您注意到有一个对象集合,而我想要一个对象。 另外,我希望键名称是字段中各个属性的名称。

我很难搞清楚如何在我的自定义转换器中使用 JToken、JProperty、JValue 对象。事实上,我从来没有尝试过这样的事情,所以我发现很难理解这些概念。

我尝试创建两个自定义转换器,一个在类的范围内,以防止生成集合,第二个在集合的范围内,但到目前为止没有成功。

这是我尝试过的:

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var token = JToken.FromObject(value);

        var key = token["Key"];
        if (key == null)
            throw new Exception("missing key");

        var propertyName = key.ToString();

        var json = new StringBuilder();
        json.Append("{");

        foreach (var child in token.Children())
        {
            var property = child as JProperty;
            if (property == null || property.Name == "Key")
                continue;

            var propName = property.Name;
            var propValue = JsonConvert.SerializeObject(property.Value);
            json.AppendFormat("\"{0}\": {1},", propName, propValue);
        }

        if (json.Length > 1)
            json.Remove(json.Length - 1, 1);
        json.Append("}");

        var newToken = JToken.Parse(json.ToString());
        var serializedObject = JsonConvert.SerializeObject(newToken);

        writer.WriteStartObject();
        writer.WritePropertyName(propertyName);
        writer.WriteToken(newToken.CreateReader());
        writer.WriteEndObject();
    }

有没有办法实现我想要实现的目标?

也许我以错误的方式解决了这个问题,所以如果您有更简单的替代方案,请务必毫不犹豫地分享您的想法。

最佳答案

您走在正确的轨道上。您在这里只需要一个转换器——用于 JsonField 对象的列表——而且代码实际上非常简单。这就是您所需要的:

class JsonFieldListConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(List<JsonField>));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JObject containerObj = new JObject();
        foreach (JsonField field in (List<JsonField>)value)
        {
            JObject itemObj = new JObject();
            itemObj.Add("Value", JToken.FromObject(field.Value));
            itemObj.Add("OtherParam", new JValue(field.OtherParam));
            containerObj.Add(field.Key, itemObj);
        }
        containerObj.WriteTo(writer);
    }

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

这是一个展示转换器运行的演示:

class Program
{
    static void Main(string[] args)
    {
        JsonPayload payload = new JsonPayload
        {
            Item1 = "Value1",
            Item2 = "Value2",
            Fields = new List<JsonField>
            {
                new JsonField { Key = "Key1", Value = "Value1", OtherParam = "other1" },
                new JsonField { Key = "Key2", Value = 42, OtherParam = "other2" },
            }
        };

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.Converters.Add(new JsonFieldListConverter());
        settings.Formatting = Formatting.Indented;

        string json = JsonConvert.SerializeObject(payload, settings);
        Console.WriteLine(json);
    }
}

输出:

{
  "Item1": "Value1",
  "Item2": "Value2",
  "Fields": {
    "Key1": {
      "Value": "Value1",
      "OtherParam": "other1"
    },
    "Key2": {
      "Value": 42,
      "OtherParam": "other2"
    }
  }
}

关于c# - 使用自定义 JsonConverter 来改变对象部分的序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22230440/

相关文章:

c# - 在列表中查找匹配项的最简洁方法

c# - SQL 从其他表中获取匹配值并显示在主表中

serialization - 使用 Json.net 往返 F# 可区分联合时出现问题

C# 使用 Newtonsoft.Json 将 JSON 字符串反序列化为对象

c# - 空 DataTable 到 Json 并包含列名称

c# - 使用 C# 如何将 RGB 转换为 YCrCb 以及将 YCrCb 转换为 RGB

C#项目最小引用集

c# - 评估方法 System.Linq.Enumerable.ToList() 调用 QuickWatch 中的本地方法 Interop+Kernel32.FindStringOrdinal()

node.js - Nuxt 3 应用程序 GitHub Actions Build 中的 NewtonSoft.Json 异常

c# - 使用 Newtonsoft.Json 解析 JSON 时出错