在使用JSON.Net进行反序列化或调用默认序列化程序时,如何将对象类型更改为另一个?

时间:2016-09-01 07:57:53

标签: c# .net serialization json.net

当序列化时,我将ClassName写入对象的_CurrentClassName属性。当使用JSON.Net库读取json时,我需要将对象更改为此属性的值。

{
 "Field1": 0,
 "Field2": "34",
 "_CurrentClassName": "MyCustomClass"
}


class CustomJsonConverter : JsonConverter
{
...
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsClass;
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var value = existingValue;
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }
        else if (reader.TokenType == JsonToken.StartObject)
        {
            JObject jObject = JObject.Load(reader);
            JToken jToken;
            if (jObject.TryGetValue("_CurrentClassName", out jToken))
            {
                var t = jToken.Value<string>();
                Type tt = Type.GetType(objectType.Namespace + "." + t);
                value = Activator.CreateInstance(tt);
                return value;
            }
        }
        return serializer.Deserialize(reader);            
    }

...
}

2 个答案:

答案 0 :(得分:0)

一旦推断出对象类型并且实例化了对象,就可以使用JsonSerializer.Populate(jObject.CreateReader())来填充它。

例如:

from socketserver import ThreadingMixIn
from http.server import BaseHTTPRequestHandler, HTTPServer
import time
from gpiozero import DigitalOutputDevice
import logging
from time import sleep
logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO)

hostName = ''
hostPort = 9001
busy = False

class ThreadingServer(ThreadingMixIn, HTTPServer):
    pass

class MyServer(BaseHTTPRequestHandler):
    def do_GET(self):
        global busy
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(bytes("Hello!<br>", "utf-8"))
        if self.path == '/gates':
           if not busy:
             busy = True
             relay = DigitalOutputDevice(17) # Initialize GPIO 17
             relay.on()
             logging.info('Cycle started')
             self.wfile.write(bytes("Cycle started<br>", "utf-8"))
             sleep(2)
             relay.close()
             sleep(20)
             relay = DigitalOutputDevice(17)
             relay.on()
             sleep(2)
             relay.close()
             logging.info('Cycle finished')
             self.wfile.write(bytes("Cycle finished", "utf-8"))
             busy = False
           else:
#             self.wfile.write(bytes("Busy now!<br>", "utf-8"))
             self.send_error(503)

myServer = ThreadingServer((hostName, hostPort), MyServer)
print(time.asctime(), "Server Starts - %s:%s" % (hostName, hostPort))

try:
    myServer.serve_forever()
except KeyboardInterrupt:
    pass

myServer.server_close()
print(time.asctime(), "Server Stops - %s:%s" % (hostName, hostPort))

CustomCreationConverter<T>也使用public abstract class CustomJsonConverterBase : JsonConverter { protected abstract Type InferType(JToken token, Type objectType, object existingValue); public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var token = JToken.Load(reader); var actualType = InferType(token, objectType, existingValue); if (existingValue == null || existingValue.GetType() != actualType) { var contract = serializer.ContractResolver.ResolveContract(actualType); existingValue = contract.DefaultCreator(); } using (var subReader = token.CreateReader()) { // Using "populate" avoids infinite recursion. serializer.Populate(subReader, existingValue); } return existingValue; } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } public class CustomJsonConverter : CustomJsonConverterBase { public const string ClassPropertyName = @"_CurrentClassName"; protected override Type InferType(JToken token, Type objectType, object existingValue) { if (token is JObject) { var typeName = (string)token[ClassPropertyName]; if (typeName != null) { var actualType = Type.GetType(objectType.Namespace + "." + typeName); if (actualType != null) return actualType; } } return objectType; } public override bool CanConvert(Type objectType) { throw new NotImplementedException(); } } 来填充刚刚分配的对象,因此这是解决此问题的标准方法。

请注意,您正在部分复制Json.NET的内置TypeNameHandling功能。

答案 1 :(得分:0)

如果您未与_CurrentClassName属性名称或其值语法结合,则可以使用Json.Net内置的类型处理。

序列化或反序列化时,您可以传入控制序列化或反序列化的JsonSerializerSettings对象。

在此对象上,您可以设置一个TypeNameHandling属性,该属性控制Json.Net如何序列化和反序列化正在处理的确切类型。

以下是LINQPad示例:

void Main()
{
    var t = new Test { Key = 42, Value = "Meaning of life" };

    var json = JsonConvert.SerializeObject(
        t, Newtonsoft.Json.Formatting.Indented,
        new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
    Console.WriteLine(json);

    var obj  =JsonConvert.DeserializeObject(json,
        new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
    Console.WriteLine(obj.GetType().FullName);
}

public class Test
{
    public int Key { get; set; }
    public string Value { get; set; }
}

输出:

{
  "$type": "UserQuery+Test, LINQPadQuery",
  "Key": 42,
  "Value": "Meaning of life"
}
UserQuery+Test

在这里,您可以看到反序列化返回的对象类型是Test类。