我在两个项目中有相同的类,一个在runtime
上发送到另一个进程,该进程必须反序列化该对象并使用它(假设这两个对象相同,但程序集名称不同,因此它们实际上被解释为两种不同的类型)。根据我的研究,我提出了一些不起作用的解决方案,原因如下。
Json.NET
:给我一个例外,这两种类型不兼容(尝试在序列化设置中使用typename.all)。
protobuf-net
:要求我在每个位置添加属性,或者简单地为它提供属性名称(在v2中),由于我的对象太复杂,这两者对我来说都是不可能的。
BinaryFormatter
:与protobuf->吨属性的原因相同。
Use Common Assembly
:由于一些与我的项目架构有关的原因,我不能。
那么,有没有什么简单的方法可以序列化一个类型,然后将其反序列化为另一个类型(实际上是同一个类,但在不同的程序集中)?
是的,使用一个程序集中的类进行序列化,并使用Json.Net反序列化为另一个程序集的类是绝对可能的。事实上,这是序列化的主要用例之一——在不同系统之间传输数据。
你需要记住两件事:
- 如果源程序集和目标程序集不同,则应而不是在JSON中包含实际的完全限定类型名称作为元数据。换句话说,请确保
TypeNameHandling
设置设置为None
(我认为这是默认设置)。如果包含类型名称元数据,那么Json.Net将期望在接收端找到这些程序集,并且反序列化将失败,因为不存在这些程序集 - 如果在类结构中使用接口而不是具体类型,则需要创建一个或多个
JsonConverter
类来处理反序列化。当Json.Net看到一个接口时,它将不知道要创建什么类型的具体类,因为它可以是任何东西。转换器可以查找JSON中可能存在的其他数据,并告诉JSON.Net要实例化哪个具体类。如果JSON中没有明显的数据段可以用作类型的指示符,则可以使用序列化端的转换器来添加自定义指示符
下面是一些示例代码来演示我所阐述的概念。演示分为两部分。第一部分是"sender",它将组成的"diagram"类结构序列化为JSON并将其写入文件。第二部分是"接收器",它读取文件并将JSON反序列化为一组不同的类。您会注意到,我有意使接收方中的一些类名与发送方不同,但它们具有相同的属性名和结构,因此它仍然有效。您还将注意到,接收器程序使用自定义JsonConverter
来处理创建正确的IFigure
实例,使用JSON中的某些属性作为指示符。
发件人
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace Sender
{
class Program
{
static void Main(string[] args)
{
Diagram diagram = new Diagram
{
Title = "Flowchart",
Shapes = new List<IShape>
{
new Circle
{
Id = 1,
Text = "Foo",
Center = new Point { X = 1, Y = 5 },
Radius = 1.25
},
new Line
{
Id = 2,
A = new Point { X = 2.25, Y = 5 },
B = new Point { X = 4, Y = 5 }
},
new Rectangle
{
Id = 3,
Text = "Bar",
TopLeft = new Point { X = 4, Y = 6.5 },
BottomRight = new Point { X = 8.5, Y = 3.5 }
}
}
};
string json = JsonConvert.SerializeObject(diagram, Formatting.Indented);
File.WriteAllText(@"C:temptest.json", json);
}
}
class Diagram
{
public string Title { get; set; }
public List<IShape> Shapes { get; set; }
}
interface IShape
{
int Id { get; set; }
string Text { get; set; }
}
abstract class AbstractShape : IShape
{
public int Id { get; set; }
public string Text { get; set; }
}
class Line : AbstractShape
{
public Point A { get; set; }
public Point B { get; set; }
}
class Rectangle : AbstractShape
{
public Point TopLeft { get; set; }
public Point BottomRight { get; set; }
}
class Circle : AbstractShape
{
public Point Center { get; set; }
public double Radius { get; set; }
}
class Point
{
public double X { get; set; }
public double Y { get; set; }
}
}
以下是Sender程序生成的JSON输出文件:
{
"Title": "Flowchart",
"Shapes": [
{
"Center": {
"X": 1.0,
"Y": 5.0
},
"Radius": 1.25,
"Id": 1,
"Text": "Foo"
},
{
"A": {
"X": 2.25,
"Y": 5.0
},
"B": {
"X": 4.0,
"Y": 5.0
},
"Id": 2,
"Text": null
},
{
"TopLeft": {
"X": 4.0,
"Y": 6.5
},
"BottomRight": {
"X": 8.5,
"Y": 3.5
},
"Id": 3,
"Text": "Bar"
}
]
}
接收器
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Receiver
{
class Program
{
static void Main(string[] args)
{
string json = File.ReadAllText(@"C:temptest.json");
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new FigureConverter());
Chart chart = JsonConvert.DeserializeObject<Chart>(json, settings);
Console.WriteLine(chart);
Console.ReadKey();
}
}
class FigureConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IFigure));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
if (jo["Center"] != null)
{
return jo.ToObject<Circle>(serializer);
}
else if (jo["TopLeft"] != null)
{
return jo.ToObject<Rectangle>(serializer);
}
else
{
return jo.ToObject<Line>(serializer);
}
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
class Chart
{
public string Title { get; set; }
public List<IFigure> Shapes { get; set; }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("Chart: ");
sb.AppendLine(Title);
foreach (IFigure figure in Shapes)
{
sb.AppendLine(figure.ToString());
}
return sb.ToString();
}
}
interface IFigure
{
int Id { get; set; }
string Text { get; set; }
}
abstract class AbstractFigure : IFigure
{
public int Id { get; set; }
public string Text { get; set; }
}
class Line : AbstractFigure
{
public Point A { get; set; }
public Point B { get; set; }
public override string ToString()
{
return string.Format("Line: A = {0}, B = {1}", A, B);
}
}
class Rectangle : AbstractFigure
{
public Point TopLeft { get; set; }
public Point BottomRight { get; set; }
public override string ToString()
{
return string.Format("Rectangle: TopLeft = {0}, BottomRight = {1}", TopLeft, BottomRight);
}
}
class Circle : AbstractFigure
{
public Point Center { get; set; }
public double Radius { get; set; }
public override string ToString()
{
return string.Format("Circle: Center = {0}, Radius = {1}", Center, Radius);
}
}
class Point
{
public double X { get; set; }
public double Y { get; set; }
public override string ToString()
{
return string.Format("({0:0.##}, {1:0.##})", X, Y);
}
}
}
以下是接收器程序的输出:
Chart: Flowchart
Circle: Center = (1, 5), Radius = 1.25
Line: A = (2.25, 5), B = (4, 5)
Rectangle: TopLeft = (4, 6.5), BottomRight = (8.5, 3.5)