【问题标题】:Determine type during json deserialize在 json 反序列化期间确定类型
【发布时间】:2016-07-31 01:29:52
【问题描述】:

我正在研究一种协议,在该协议中接收器将接收某些指定自定义类型的 json 消息(当前为 5,但可能是 10-20)。我正在努力想出一个最佳/快速的解决方案,它会自动反序列化 json 并返回正确的对象类型。

例子:

public class MessageA
{
    public string Message;
} 

public class MessageB
{
    public int value;
}

public class MessageC
{
    public string ValueA;
    public string ValueB;
}

理想情况下,方法应该是这样的

 Object Deserialize(string json);

它将返回三种消息类型之一或 null - 以防出现解析错误/json 不匹配任何预定义类型。

更新:我可以控制发送方/接收方以及协议设计。

【问题讨论】:

  • 您是发件人还是收件人,还是您可以控制两者的内容?
  • 我的解决方案是将类名作为字符串发送到 Json 文件中,第一次作为 JObject 反序列化以确定类型,第二次创建对象

标签: c# json serialization json.net


【解决方案1】:

如果消息可以指定其类型会很有帮助。否则,您必须从某些属性或其他属性中推断出来。

您可以在序列化时使用消息包装类,如下所示:

public class MessageWrapper<T>
{
    public string MessageType { get { return typeof(T).FullName; } }
    public T Message { get; set; }
}

因此,如果您有一个具有 FirstLast 属性的类 Name,则可以像这样对其进行序列化:

var nameMessage = new MessageWrapper<Name>();
nameMessage.Message = new Name {First="Bob", Last = "Smith"};
var serialized = JsonConvert.SerializeObject(nameMessage);

序列化的 JSON 是

{"MessageType":"YourAssembly.Name","Message":{"First":"Bob","Last":"Smith"}}

反序列化时,先将JSON反序列化为这种类型:

public class MessageWrapper
{
    public string MessageType { get; set; }
    public object Message { get; set; }
}

var deserialized = JsonConvert.DeserializeObject<MessageWrapper>(serialized);

MessageType 属性中提取消息类型。

var messageType = Type.GetType(deserialized.MessageType);

现在您知道类型,您可以反序列化 Message 属性。

var message = JsonConvert.DeserializeObject(
    Convert.ToString(deserialized.Message), messageType);

messageobject,但您可以将其转换为 Name 或它实际的任何类。

【讨论】:

  • 很好的答案,它可以从 var 消息转换为“Name”吗?当然,没有明确表示为 DeserializeObject()
  • 我正在尝试类似的方法,但尝试将 object Message 转换为具体类,但没有成功。您指出的技巧是先将属性 Message.ToString() 转换为然后将该 JSON 字符串反序列化为您的具体类。
【解决方案2】:
var log = JsonConvert.DeserializeObject<Log>(File.ReadAllText("log.example.json");

public class Log
{
    [JsonConverter(typeof(MessageConverter))]
    public object[] Messages { get; set; }
}


public class MessageA
{
    public string Message;
}
public class MessageB
{
    public int value;
}
public class MessageC
{
    public string ValueA;
    public string ValueB;
}

public class MessageConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object ReadMessage(JObject jobject)
        {
            if (jobject.Property("Message") != null)
                return jobject.ToObject<MessageA>(serializer);
            if (jobject.Property("value") != null)
                return jobject.ToObject<MessageB>(serializer);
            if (jobject.Property("ValueA") != null)
                return jobject.ToObject<MessageC>(serializer);
            throw new Exception("Type is not recognized");
        }

        var jarray = JArray.Load(reader);
        return jarray.Select(jitem => ReadMessage((JObject)jitem)).ToArray();
    }


    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Json 示例:

{
  "Messages":
  [
    {"Message": "System halted"},
    {"value": 42},
    {"ValueA": "Bob", "ValueB": "Smith"}
  ]
}

【讨论】:

    【解决方案3】:

    希望您熟悉工厂模式,您可以使用工厂并在 json 中包含一个“类型”属性,我们称之为_t

    您可以自己解析 json 字符串并找到 _t 属性的值,将其反序列化为 dynamic 并获取 jsonObj._t 或使用仅包含 _t 字段的简单 class 来反序列化json 到最初。

    然后,您可以将代表 C# 类型的 string 传递给工厂,并为该 Type 获取 json 反序列化器。

    然后,您可以让所有呼出和呼入分别添加和处理 _t 参数,以便将来轻松添加新类型,只需添加和注册您需要的序列化器/反序列化器 @987654330 @与工厂。

    【讨论】:

      【解决方案4】:

      作为一个选项,您可以检查 JSON 字符串是否包含特定于类的属性名称。快速简单!

      【讨论】:

      • 你看前面的答案了吗?其中一个建议相同,但具有良好的代码示例
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-17
      • 1970-01-01
      • 2013-12-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多