【问题标题】:How to serialize/deserialize from one type to another?如何从一种类型序列化/反序列化到另一种类型?
【发布时间】:2019-08-02 03:07:21
【问题描述】:

解释上下文的小介绍:

在服务器端:

  • 我有一个程序集“TheoreticalObjects”
  • 此程序集包含一个基类“BaseClass”,我的所有 THEORETICAL 对象都从该基类派生
  • 从 TheoreticalObjects.BaseClass 继承的类例如: TheoreticalObjects.Tube、TheoreticalObjects.Flange、TheoreticalObjects.Caps...

在客户端:

  • 我也有理论对象的组装
  • 但我还有一个名为“RealObjects”的程序集(专用于生成顶点)
  • 此程序集包含一个基类“BaseClass”,我的所有 REAL 对象都从该基类派生
  • 从 RealObjects.BaseClass 继承的类例如: RealObjects.Tube、RealObjects.Flange、RealObjects.Caps...

我想在服务器上序列化我的对象(作为 TheoreticalObjects.BaseClass),通过 tcp 将 json 发送到客户端,并反序列化 json(作为 RealObjects.BaseClass)。

这是我的课程:(假设我们要创建一个 Tube):

// my TheoreticalObjects.BaseClass
namespace TheoreticalObjects
{
    [DataContract]
    public class BaseClass
    {
        [DataMember]
        public Guid GUID { get; set; }

        [DataMember]
        public int ID { get; set; }

        [DataMember]
        public string Designation { get; set; }

        [DataMember]
        public string Product { get; set; }

        [DataMember]
        public int IDMaterial { get; set; }

        [DataMember]
        public int Quantity { get; set; }

        [DataMember]
        public string Form { get; set; }

        protected BaseClass()
        { }
        protected BaseClass(int iD, string designation, string product, int iDMaterial, int quantity, string form) 
        {
            ID = iD;
            Designation = designation;
            Product = product;
            IDMaterial = iDMaterial;
            Quantity = quantity;
            Form = form;
        }
    }
}
// my TheoreticalObjects.Tube
namespace TheoreticalObjects
{
    [DataContract]
    public class Tube : BaseClass
    {
        [DataMember]
        public Length Diameter { get; set; }

        [DataMember]
        public Length WallThickness { get; set; }

        [DataMember]
        public Length Length { get; set; }

        public Tube() : base()
        { }

        public Tube(int iD, string designation, string product, int iDmaterial, int quantity, string form, Length diameter, Length Wallthickness, Length length)  : base(iD, designation, product, iDmaterial, quantity,form)
        {
            WallThickness = Wallthickness;
            Diameter = diameter;
            Length = length;
        }

    }
}
// my RealObjects.BaseClass
namespace RealObjects
{
    public class BaseClass
    {

        public Guid GUID { get; set; }
        public int ID { get; set; }
        public string Designation { get; set; }
        public string Product { get; set; }
        public int IDMaterial { get; set; }
        public int Quantity { get; set; }
        public string Form { get; set; }


        protected BaseClass() { }
        protected BaseClass(int iD, string designation, string product, int iDMaterial, int quantity, string form)
        {
            ID = iD;
            Designation = designation;
            Product = product;
            IDMaterial = iDMaterial;
            Quantity = quantity;
            Form = form;
        }


        public List<Face> myFaces = new List<Face>(); // faces of the mesh
        public MyMesh mesh = new MyMesh();

        public void Triangulation(TopoDS_Shape shape, double deflection)
        {
            // things ...


        myFaces = things...
            mesh = new MyMesh(myFaces);
        }


    }
}
// my RealObjects.Tube
namespace RealObjects
{
    public class Tube: BaseClass
    {

        public double diameter;
        public double Wallthickness;
        public double length;

        public Tube() : base() { }
        public Tube(int iD, string designation, string product, int iDmaterial, int quantity, string form, double diameter, double wallThickness, double length) : base(iD, designation, product, iDmaterial, quantity, form)
        {
            this.diameter = diameter;
            this.Wallthickness = wallThickness;
            this.length = length;

            Build(diameter, Wallthickness, length);
        }

        public void Build(double diameter, double Wallthickness, double length)
        {
           //things ...


           Triangulation(things...);
        }


    }
}

我的问题是,在将我的 Tube 序列化并发送到客户端后,它没有正确反序列化:我得到的是 RealObjects.BaseClass 而不是 RealObjects.BaseClass.Tube。

  • 我做了一个 Binder 来将名称绑定到类型,但是反序列化时根本没有调用 BindToType()

_______________在服务器端____________

//creating the Tube  
TheoreticalObjects.Tube c = new TheoreticalObjects.Tube(1, "Tube", "Element", 1, 1, "tube", new Length(1, UnitsNet.Units.LengthUnit.Meter), new Length(0.1, UnitsNet.Units.LengthUnit.Meter), new Length(2, UnitsNet.Units.LengthUnit.Meter));
// settings for the serializer
JsonSerializerSettings _jsonSerializerSettingsOCCServer = new JsonSerializerSettings { Formatting = Newtonsoft.Json.Formatting.Indented };
_jsonSerializerSettingsOCCServer.Converters.Add(new UnitsNetJsonConverter());
// serialization
string json = JsonConvert.SerializeObject(c, _jsonSerializerSettingsOCCServer).Replace("\r\n", "\n");
// the message that the server will send
CommunicateElement messageObject = new CommunicateElement(NetworkComms.NetworkIdentifier, json, 1234, c.Designation);

然后发送消息

_______________在客户端____________

消息被处理,一个函数将消息放入“constructionQueue”

// settings for the deserializer
_jsonSerializerSettingsOCC = new JsonSerializerSettings {
            TypeNameHandling = TypeNameHandling.All,
            Binder = new MyBinder(),
            NullValueHandling = NullValueHandling.Ignore,
            DefaultValueHandling = DefaultValueHandling.Ignore,
            Formatting = Newtonsoft.Json.Formatting.Indented,
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };
_jsonSerializerSettingsOCC.Converters.Add(new UnitsNetJsonConverter());
// deserialize the json (that was previously a TheoreticalObjects.Tube) into a RealObjects.BaseClass and add it to the construction queue
constructionQueue.Add(JsonConvert.DeserializeObject<RealObjects.BaseClass>(messageObject.Message, _jsonSerializerSettingsOCC));

…… ......一旦它在施工队列中,我尝试创建它...... ......不需要知道之后会发生什么...... ......请注意,我正在寻找 RealObjects.Tube 而不是 RealObjects.BaseClass ..... ......

_______________ MyBinder ____________

public class MyBinder : SerializationBinder
    {
        readonly Dictionary<Type, string> typeToName = new Dictionary<Type, string>();
        readonly Dictionary<string, Type> nameToType = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);

        public MyBinder()
        {
            List<Type> myTypes = new List<Type>();
            Assembly[] myAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            for (int i = 0; i < myAssemblies.Length; i++)
            {
                if (myAssemblies[i].GetName().Name == "RealObjects")
                {
                    foreach (Type t in myAssemblies[i].GetTypes())
                    {
                        if (t.IsSubclassOf(typeof(RealObjects.BaseClass)))
                        {
                            myTypes.Add(t);
                        }
                    }
                    break;
                }
            }

            foreach (var type in myTypes)
            {
                Map(type, type.Name);
            }
        }

        public void Map(Type type, string name)
        {
            this.typeToName.Add(type, name);
            this.nameToType.Add(name, type);
        }


        public Type Get(string typeName)
        {
            return nameToType[typeName];
        }

        public string Get(Type type)
        {
            return typeToName[type];
        }

        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
        {
            // we retrieve the name in the RealObjects assembly
            typeName = Get(serializedType);
            assemblyName = "RealObjects";
        }

        public override Type BindToType(string assemblyName, string typeName)
        {
            return Get(typeName);
        }
    } // credit: https://stackoverflow.com/questions/11099466/using-a-custom-type-discriminator-to-tell-json-net-which-type-of-a-class-hierarc

我无法调用构造函数,因为我的 RealObjects.BaseClass 没有我的 RealObjects.Tube 所拥有的 Diameter、wallThickness 和 Length 字段,并且在反序列化为 RealObjects.BaseClass 时我丢失了它们的值

// called after being added to the construction queue
private void CreateObject(RealObjects.BaseClass c)
    {
        MyBinder binder = new MyBinder();
        Type type = binder.BindToType("RealObjects", c.Designation); 
        ConstructorInfo[] ctor = type.GetConstructors();
        BasicClass be;
        foreach (ConstructorInfo ci in ctor)
        {
            try
            {
                object instance = ci.Invoke(new object[] { c });
                be = (BasicClass )instance;
            } catch (Exception e)
            {
                Debug.Log(e.ToString());
            }

        }

        // things...

    }

所有建议都是开放的

我希望我的英语不是太差,并且我已经清楚地解释了自己,谢谢你的帮助

【问题讨论】:

  • 我认为问题主要来自这样一个事实,即当它应该将 json 动态反序列化为 RealObjects.Tube 时没有调用活页夹,而是我得到了一个 RealObjects.BaseClass,其中的直径、长度和 wallTichkness 不存在(这就是为什么我以后不能将其转换为 Tube)
  • 您的代码无法编译(LengthUnitsNetJsonConverter 没有定义)所以没有 minimal reproducible example,但您似乎没有为 root 输出多态类型信息序列化时在服务器端的对象。为此,请参阅Serializing an interface/abstract object using NewtonSoft.JSON。但请注意接受的答案中提到的安全性。
  • 链接答案的粗略演示:dotnetfiddle.net/SgBcWK。如果您想确保 $type 仅出现在根目录上,请参阅json.net - how to add property $type ONLY on root object
  • Length 和 UnitsNetJsonConverter 分别来自于类。 UnitsNet.dll 和 UnitsNet.Serialization.JsonNet.dll。

标签: c# serialization deserialization


【解决方案1】:

谜题中缺少的部分是缺少“根对象的多态类型信息”的序列化方式,需要添加如this answerSerializing an interface/abstract object using NewtonSoft.JSON dbc:

TheoreticalObjects.Tube c = new TheoreticalObjects.Tube(1, "Tube", "Element", 1, 1, "tube", new Length(1, UnitsNet.Units.LengthUnit.Meter), new Length(0.1, UnitsNet.Units.LengthUnit.Meter), new Length(2, UnitsNet.Units.LengthUnit.Meter));
JsonSerializerSettings _jsonSerializerSettingsOCCServer = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Newtonsoft.Json.Formatting.Indented };
_jsonSerializerSettingsOCCServer.Converters.Add(new UnitsNetJsonConverter());
string json = JsonConvert.SerializeObject(c, typeof(TheoreticalObjects.BaseClass), _jsonSerializerSettingsOCCServer);

反序列化部分的设置与我的第一篇文章相同。 但是 binder 的构造函数发生了变化:

public MyBinder()
{
    List<Type> myTypes = new List<Type>();
    Assembly[] myAssemblies = AppDomain.CurrentDomain.GetAssemblies();
    for (int i = 0; i < myAssemblies.Length; i++)
    {
        if (myAssemblies[i].GetName().Name == "RealObjects")
        {
            foreach (Type t in myAssemblies[i].GetTypes())
            {
                if (t.IsSubclassOf(typeof(RealObjects.BaseClass)))
                {
                    myTypes.Add(t);
                }
            }
            break;
        }
    }

    foreach (var type in myTypes)
    {
        Map(type, "TheoreticalObjects."+type.Name); //this part changed
    }
}

这样你就绑定到了正确的类 (RealObjects.Tube)

我的构造队列中有一个 RealObjects.Tube 类的实例

请注意,我必须将 RealObjects.Tube 中的字段修改为 Length 类型而不是 double 类型

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多