Small intro to explain the context:
On Server side:
- I have an assembly "TheoreticalObjects"
- this assembly contains a base class "BaseClass" from which all my THEORETICAL objects are derivating
- the classes inheriting from TheoreticalObjects.BaseClass are for example: TheoreticalObjects.Tube, TheoreticalObjects.Flange, TheoreticalObjects.Caps...
On Client side:
- I have also that assembly for theoretical objects
- but I have an other assembly (dedicated to generate vertices) called "RealObjects"
- this assembly contains a base class "BaseClass" from which all my REAL objects are derivating
- the classes inheriting from RealObjects.BaseClass are for example: RealObjects.Tube, RealObjects.Flange, RealObjects.Caps...
I want to serialize my objects on the server (as TheoreticalObjects.BaseClass), send the json by tcp to the client, and deserialize the json (as RealObjects.BaseClass).
Here are my classes: (Let's suppose that we want to create a 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...);
}
}
}
My problem is that after serializing and sending my Tube to the client, it doesn't deserialize correctly: I get a RealObjects.BaseClass instead of a RealObjects.BaseClass.Tube.
- I did a Binder to bind names to types, but BindToType() isn't getting called at all when deserializing
_______________On server side____________
//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);
after that the message is sent
_______________On client side____________
the message is handled, a function put the message in a "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));
...... ...... once it is in the construction queue, I try creating it ..... ...... no need to know what happens after that ..... ...... note that i'm looking for a RealObjects.Tube and not a 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
I wasn't able to call the constructor because my RealObjects.BaseClass doesn't have the fields Diameter, wallThickness and Length that my RealObjects.Tube does have, and I loose their values when deserializing to 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...
}
All suggestions are open
I hope my english wasn't too bad, and that I explained myself clearly, thank you for any help