使用自定义JSONCONVERTER来更改对象部分的序列化



我很难超越自定义jsonconverter的写入方法,以稍微改变执行序列化的方式。

我需要调用接受具有通用部分的某些输入的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需要将字段是一个对象,其中包含与原始集合中指定的密钥属性相对应的名称的多个字段。喜欢:

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

简短答案:

您可以通过将List<T>更改为Dictionary<T>

来做到这一点

以示例为例:

您只需通过将List<T>更改为Dictionary<T>来做到这一点,这将为您提供似乎是单个对象,其中包含属性名称作为字典的键。如果类包含键,尽管您可以[JsonIgnore],但这也将被序列化。

void Main()
{
    var pl = new JsonPayload() 
    {Item1 = "Value1",Item2 = "Value2"};
    var field1 = new JsonField() { Key = "Key1", Value = "Value1", OtherParam = "other1" };
    var field2 = new JsonField() { Key = "Key2", Value = "Value2", OtherParam = "other2" };
    pl.Fields = new Dictionary<string, JsonField>() { { field1.Key , field1},  { field2.Key, field2 }}; 
    string json = JsonConvert.SerializeObject(pl);
    JsonPayload pl2 = JsonConvert.DeserializeObject<JsonPayload>(json);
    string output = JsonConvert.SerializeObject(pl2);
    output.Dump();
}
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 Dictionary<string, JsonField> Fields { get; set; }
}

相关内容

  • 没有找到相关文章

最新更新