【发布时间】: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)
-
您的代码无法编译(
Length或UnitsNetJsonConverter没有定义)所以没有 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