【问题标题】:Serialize in one assembly, and de-serialize in another?在一个程序集中序列化,在另一个程序集中反序列化?
【发布时间】:2015-02-13 22:08:16
【问题描述】:

我在两个项目中有相同的类,其中一个在 runtime 上发送到另一个必须反序列化该对象并使用它的进程(假设这两个对象相同但程序集名称不同,因此它们实际上被解释为两种不同的类型)。根据我的研究,我提出了那些由于以下原因而不起作用的解决方案。

Json.NET:给我一个例外,这两种类型不兼容(尝试在序列化设置中使用 typename.all)。

protobuf-net: 要求我在每个地方添加属性或简单地为其提供属性名称(在 v2 中),由于我的对象太复杂,这对我来说都是不可能的。

BinaryFormatter: 与 protobuf 相同的原因-> 大量属性。

Use Common Assembly:由于某些与我的项目架构相关的原因,我不能。

那么有没有简单的方法来序列化一种类型,然后将其反序列化为另一种类型(实际上是相同的类但在不同的程序集中)?

【问题讨论】:

  • 两个程序集是否可以/是否都引用了 common 程序集?
  • 我唯一能想到的另一件事是在没有任何命名空间的情况下执行 XML。到目前为止,最好的解决方案是使用通用库。
  • @DanielEugen 为什么程序集信息会被序列化?序列化的结果应该完全可以转移到另一种运行时或语言。它不应该关心源程序集元数据。
  • @DanielEugen,当使用 JSON.NET 时,请尝试使用 TypeNameHandling.None 而不是 TypeNameHandling.AllAll 值告诉序列化程序包含所有对象的类型信息,而该类型信息可能是导致不兼容错误的原因。将其设置为None 将排除所有类型信息。您可以检查序列化对象中的 $type 属性以验证是否包含类型信息。
  • @BenJespers 在 Json.NET 我尝试为反序列化程序提供 TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple 设置,该设置应该忽略程序集名称更改,但是我仍然得到 JSON 中指定的类型与异常不兼容。

标签: c# serialization json.net protobuf-net binaryformatter


【解决方案1】:

是的,绝对可以在一个程序集中使用类进行序列化,并使用 Json.Net 将其反序列化为另一个程序集的类。事实上,这首先是序列化的主要用例之一——在不同系统之间传输数据。

有两点需要牢记:

  1. 如果您的源程序集和目标程序集不同,那么您应该将实际的完全限定类型名称作为元数据包含在 JSON 中。换句话说,确保将TypeNameHandling 设置设置为None(我相信这是默认设置)。如果您包含类型名称元数据,那么 Json.Net 将期望在接收端找到这些程序集,并且反序列化将失败,因为这些程序集不存在。
  2. 如果您在类结构中使用接口而不是具体类型,则需要创建一个或多个JsonConverter 类来处理反序列化。当 Json.Net 看到一个接口时,它不知道要创建什么类型的具体类,因为它可能是任何东西。转换器可以查找 JSON 中可能存在的其他数据,并告诉 Json.Net 要实例化哪个具体类。如果 JSON 中没有明显的数据可以作为类型的指标,可以在序列化端使用转换器添加自定义指标。

这里有一些示例代码来演示我所阐述的概念。演示分为两部分。第一部分是“发送者”,它将一个组成的“图表”类结构序列化为 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:\temp\test.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:\temp\test.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);
        }
    }
}

这是 Receiver 程序的输出:

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)

【讨论】:

    猜你喜欢
    • 2012-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 2012-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多