使用自定义JsonConverter来更改对象部分的序列化 [英] Using custom JsonConverter in order to alter the serialization of the portion of an object

查看:352
本文介绍了使用自定义JsonConverter来更改对象部分的序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm having a hard time overriding the WriteJson method of a custom JsonConverter in order to slightly alter the way serialization is performed.

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

I need to call a REST service that accepts a certain input that has a generic portion. I can reproduce the problem I'm having with the following payload format :

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属性相对应.像这样:

The REST API I'm calling needs to have Fields be an object containing as many fields with names corresponding to the Key properties specified in the original collection. Like so:

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

但是,使用默认选项,有效负载按以下方式进行序列化:

However, using the default options, the payload is serialized like this:

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

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

You notice that there is a collection of objects where I would like a single object. Also, I would like the Key names to be the names of individual properties in the Fields.

我很难弄清楚如何在自定义转换器中使用JToken,JProperty和JValue对象.实际上,我从来没有尝试过这样的事情,因此我很难把这些概念包住头.

I have a hard time figuring out how to use the JToken, JProperty, JValue object in my custom converter. I have, in fact, never attempted such a thing so I find it hard to wrap my head around those concepts.

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

I have tried creating two custom converters, one at the scope of the class, in order to prevent the collection from being generated, and the second one at the scope of the collection, but so far without success.

这是我尝试过的:

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

有没有一种方法可以实现我想要实现的目标?

Is there a way to perform what I want to achieve?

也许我正在以错误的方式来解决问题,所以请,如果您有更简单的选择,那么请不要犹豫,分享您的想法.

Maybe I'm approaching the problem the wrong way, so please, if you have easier alternatives, then by all means do not hesitate to share what you have in mind.

推荐答案

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

You're on the right track. You only need a single converter here -- for the list of JsonField objects --and the code is actually very straightforward. This is all you need:

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

以下是演示转换器运行的演示:

Here's a demo showing the converter in action:

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"
    }
  }
}

这篇关于使用自定义JsonConverter来更改对象部分的序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆