【问题标题】:Serialize/Deserialize dynamic property name using JSON.NET使用 JSON.NET 序列化/反序列化动态属性名称
【发布时间】:2017-10-31 19:16:23
【问题描述】:

我有以下课程:

public class MyRequest
{
    public string Type {get;set;}
    public string Source {get;set;}
}

我想从名为 Type 的 JSON 字段中序列化/反序列化 Source 的值,例如:

{
    "type": "bank",
    "bank": "Some value"
}

{
    "type": "card",
    "card": "Some value"
}

两者都绑定到Source 属性。

【问题讨论】:

  • 我相信这可以通过使用自定义序列化程序来实现。一个例子在这里:blog.maskalik.com/asp-net/…
  • 只使用Dictionary<string, string> 进行序列化/反序列化,然后从中构造您的对象会不会更容易和更快?

标签: c# json json.net


【解决方案1】:

您可以创建一个自定义JsonConverter 来处理动态属性名称:

public class MyRequestConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(MyRequest);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        string type = (string)jo["type"];
        MyRequest req = new MyRequest
        {
            Type = type,
            Source = (string)jo[type ?? ""]
        };
        return req;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        MyRequest req = (MyRequest)value;
        JObject jo = new JObject(
            new JProperty("type", req.Type),
            new JProperty(req.Type, req.Source));
        jo.WriteTo(writer);
    }
}

要使用转换器,请向您的类添加 [JsonConverter] 属性,如下所示:

[JsonConverter(typeof(MyRequestConverter))]
public class MyRequest
{
    public string Type { get; set; }
    public string Source { get; set; }
}

这是一个有效的往返演示:https://dotnetfiddle.net/o7NDTV

【讨论】:

    【解决方案2】:

    我会编写自定义序列化/反序列化方法

    var req1 = new MyRequest() { Type = "card", Source = "SomeValue" };
    var json = Serialize(req1);
    var req2 = Deserialize<MyRequest>(json);
    

    string Serialize<T>(T obj)
    {
        var jObj = JObject.FromObject(obj);
        var src = jObj["Source"];
        jObj.Remove("Source");
        jObj[(string)jObj["Type"]] = src;
        return jObj.ToString(Newtonsoft.Json.Formatting.Indented);
    }
    
    T Deserialize<T>(string json)
    {
        var jObj = JObject.Parse(json);
        var src = jObj[(string)jObj["Type"]];
        jObj.Remove((string)jObj["Type"]);
        jObj["Source"] = src;
        return jObj.ToObject<T>();
    } 
    

    【讨论】:

      【解决方案3】:

      我最近遇到了这类问题,需要使用具有动态数据协定的 API,因此我开发了一个名为 SerializationInterceptor 的包。这是 GitHub 链接:https://github.com/Dorin-Mocan/SerializationInterceptor/wiki。您还可以使用 Nuget 包管理器安装包。

      下面的示例使用 Newtonsoft.Json 进行序列化/反序列化。当然你可以使用任何其他工具,因为这个包不依赖任何工具。

      你可以做的是创建一个拦截器:

      public class JsonPropertyInterceptorAttribute : SerializationInterceptor.Attributes.InterceptorAttribute
      {
          public JsonPropertyInterceptorAttribute(string interceptorId)
              : base(interceptorId, typeof(JsonPropertyAttribute))
          {
          }
      
          protected override SerializationInterceptor.Attributes.AttributeBuilderParams Intercept(SerializationInterceptor.Attributes.AttributeBuilderParams originalAttributeBuilderParams)
          {
              object value;
              switch (InterceptorId)
              {
                  case "some id":
                      // For DESERIALIZATION you first need to deserialize the object here having the prop Source unmapped(we'll invoke the proper deserialization later to have Source prop mapped to the correct Json key),
                      // then get the value of the prop Type and assign it to variable from below.
                      // For SERIALIZATION you need somehow to have here access to the object you want to serialize and get
                      // the value of the Type prop and assign it to variable from below.
                      value = "the value of Type prop";
                      break;
                  default:
                      return originalAttributeBuilderParams;
              }
              originalAttributeBuilderParams.ConstructorArgs = new[] { value };
              return originalAttributeBuilderParams;
          }
      }
      

      然后将拦截器放在Source prop上:

      public class MyRequest
      {
          [JsonProperty("type")]
          public string Type { get; set; }
      
          [JsonPropertyInterceptor("some id")]
          [JsonProperty("source")]
          public string Source { get; set; }
      }
      

      然后你像这样调用正确的序列化/反序列化:

      var serializedObj = SerializationInterceptor.Interceptor.InterceptSerialization(obj, objType, (o, t) =>
      {
          var serializer = new JsonSerializer { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
          using var stream = new MemoryStream();
          using var streamWriter = new StreamWriter(stream);
          using var jsonTextWriter = new JsonTextWriter(streamWriter);
          serializer.Serialize(jsonTextWriter, o, t);
          jsonTextWriter.Flush();
          return Encoding.Default.GetString(stream.ToArray());
      })
      
      var deserializedObj = SerializationInterceptor.Interceptor.InterceptDeserialization(@string, objType, (s, t) =>
      {
          var serializer = new JsonSerializer();
          using var streamReader = new StreamReader(s);
          using var jsonTextReader = new JsonTextReader(streamReader);
          return serializer.Deserialize(jsonTextReader, t);
      });
      

      【讨论】:

      • 今天我发布了一个新版本的 SerializationInterceptor(2.0.0),它支持在序列化上下文中存储参数。
      【解决方案4】:

      我的解决方案是: 首先创建 APIResultModel 类:

      public class APIResultModel<T> where T: APIModel, new()
      {
      
          public string ImmutableProperty { get; set; }
      
          public T Result { get; set; }
      
          public APIResultModel<T> Deserialize(string json)
          {
              var jObj = JObject.Parse(json);
              T t = new T();
              var result = jObj[t.TypeName()];
              jObj.Remove(t.TypeName());
              jObj["Result"] = result;
              return jObj.ToObject<APIResultModel<T>>();
          }
      }
      

      第二次创建APIModel抽象类:

      public abstract class APIModel
      {
          public abstract string TypeName();
      }
      

      第三次创建动态内容模型类:

      public class MyContentModel: APIModel
      {
          public string Property {get; set;}
          public override string TypeName()
          {
              return "JsonKey";
          }
      }
      

      当你需要反序列化一个json字符串时:

      var jsonModel = new APIResultModel<MyContentModel>();
      jsonModel = jsonModel.Deserialize(json);
      MyContentModel dynimacModel = jsonModel.Result;
      

      反序列化函数来自@Eser

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-20
        • 1970-01-01
        • 2015-11-22
        相关资源
        最近更新 更多