json反序列化时判断类型

问题描述 投票:0回答:5

我正在开发一个协议,其中接收者将接收某些指定自定义类型(当前为 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;
}

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

 Object Deserialize(string json);

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

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

c# json serialization json.net
5个回答
26
投票

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

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

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

因此,如果您有一个带有

Name
First
属性的类
Last
,您可以像这样序列化它:

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
或它实际上是什么类别。


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
,或者使用一个简单的
class
,仅包含
_t
字段,仅将 json 反序列化为最初。

然后您可以将代表 C# 类型的

string
传递给工厂,并为其获取 json 反序列化器
Type

然后,您可以使所有呼出和呼入呼叫分别添加和处理

_t
参数,以便将来可以轻松添加新类型,只需添加和注册该
Type
所需的序列化器/反序列化器即可工厂。


0
投票

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


0
投票

我知道这是一个老问题,但我努力寻找对我有用的答案。这就是我最终的结果...

创建一个新的空界面

public interface IMessage { }

并继承该接口

public class MessageA : IMessage
{
   public string Message;
}

然后创建我自己的序列化器

using Newtonsoft.Json;
public class TypedJsonSerializer
{
    private readonly JsonSerializerSettings _settings;

    public TypedJsonSerializer()
    {
        _settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects };
    }

    public string Serialize<TType>(TType obj)
    {
        return JsonConvert.SerializeObject(obj, _settings);
    }

    public TType Deserialize<TType>(string serialized)
    {
            return JsonConvert.DeserializeObject<TType>(serialized, _settings);
    }
}

反序列化后,可以使用“is”运算符测试生成的对象并进行适当的转换。

var msg = myTypedSerializer.Deserialize<IMessage>(messageJson);

if (msg is MessageA)
{
    var myMsgA = (MessageA)msg;
}
© www.soinside.com 2019 - 2024. All rights reserved.