JSON反序列化过程中如何确定类型

13

我正在开发一个协议,接收方将会收到指定的自定义类型(目前为5种,但可能会增加到10-20种)的JSON消息。我在努力想出一种最优/快速的解决方案,可以自动反序列化JSON并返回正确类型的对象。

示例:

public class MessageA
{
    public string Message;
} 

public class MessageB
{
    public int value;
}

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

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


(Ideally, the method should be like)
 Object Deserialize(string json);

它将返回三种信息类型之一或 null - 如果存在解析错误/JSON未匹配任何预定义类型。

更新:我对发送者/接收者以及协议设计有控制权。


你是发件人、收件人还是对两者的内容都有控制权? - starlight54
我的解决方案是在Json文件中以字符串形式发送类名,第一次反序列化为JObject以确定类型,第二次创建对象。 - Zimo
4个回答

24

如果消息能够指定其类型,那将非常有帮助。否则,您需要从某个属性或另一个属性中推断出它的类型。

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

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

message是一个object,但你可以将它转换为Name或者它实际所属的类。


好的回答,它可以从var message转换为“Name”吗?当然可以,而不必显式地使用DeserializeObject<Name>()。 - Leandro Bardelli
我曾尝试过类似的方法,但是试图将“object Message”强制转换为具体类并没有成功。正如你指出的那样,诀窍在于首先将属性“Message.ToString()”转换为JSON字符串,然后将其反序列化为具体类。 - JohnB

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

1
希望您熟悉工厂模式,您可以使用工厂并将“Type”属性作为json的一部分包含在内,让我们称其为_t。您可以自己解析json字符串并查找_t属性的值,将其反序列化为dynamic并获取jsonObj._t,或者只有一个_t字段的简单class,仅用于最初反序列化json。然后,您可以将表示C# Type的string传递给工厂,并获得该Type的json反序列化器。然后,您可以使所有出站和入站调用分别添加和处理_t参数,以便未来轻松添加新类型,只需使用所需的工厂注册相应的序列化程序/反序列化程序即可。

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

3
你读了之前的回答吗?其中一个建议相同,但提供了良好的代码示例。 - AndrewSilver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接