I have a class named clsTest which is defined as:
public class clsTest
{
public string Name;
public string Family;
public int Age;
}
I have another class named clsMain which is Serializing three instances of clsTest class to JSON as:
public class clsMain
{
public string mtdMain()
{
clsTest ct1_a = new clsTest();
clsTest ct1_b = new clsTest();
clsTest ct1_c = new clsTest();
ct1_a.Name = "Satoshi";
ct1_a.Family = "Nakamato";
ct1_b.Name = "Charles";
ct1_b.Family = "Hoskinson";
ct1_b.Age = 33;
ct1_c.Name = "AmirAli";
ct1_c.Family = "Sam";
ct1_c.Age = 25;
List<clsTest> lst = new List<clsTest>();
lst.Add(ct1_a);
lst.Add(ct1_b);
lst.Add(ct1_c);
JsonSerializerOptions option = new JsonSerializerOptions();
option.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
option.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
return JsonSerializer.Serialize(lst, option);
}
}
When I debug the project my list is full as shown in the screenshot:
But at the end return JsonSerializer.Serialize(lst, option); serialize as below:
I couldn't find the problem, any suggestion will be appreciated.
Thanks.
Use properties instead of fields, like so:
public class Test
{
public string Name { get; set; }
public string Family { get; set; }
public int Age { get; set; }
}
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
The missing part of the puzzle was the way of serializing that was missing the "polymorphic type information for the root object", which needed to be added as shown in this answer to Serializing an interface/abstract object using NewtonSoft.JSON by 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);
The settings for the deserialization part remains the same as on my first post.
But the constructor of the binder changes:
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
}
}
that way you bound to the right class (RealObjects.Tube)
I was having an instance of the class RealObjects.Tube in my construction queue
Note that I had to modify the fields in my RealObjects.Tube to be of Length type and not double type
I want to create some classes, that have to look a certain way ( the way shown in my example).
Some of the class properties are classes (or structs) themselves.
I want to write a method within my classes that get the Property-Values of all the Properties, that are Structs and write them to a string.
So this is what my classes look like:
public class car
{
public string brand { get; set; }
public tire _id { get; set; }
public string GetAttributes()
{
Type type = this.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach(PropertyInfo propertyInfo in properties)
if (propertyInfo.PropertyType.ToString().Contains("_"))
{
//I want to write the actual value of the property here!
string nested_property_value = ...
return nested_property_value;
}
}
}
this is what my structs look like:
public struct tire
{
public int id { get; set; }
}
this would be the Main Program:
tire mynewtire = new tire()
{
id = 5
};
car mynewcar = new car()
{
_id = mynewtire
};
Anyone has an idea how to create the GetAttributes-Methode? I've been trying to figure this out for ages now, but don't get there...
This code will get you started. I recommend you look at other serialisation methods (such as JSON) as well.
using System;
namespace Test
{
public class car
{
public string brand { get; set; }
public tire _id { get; set; }
public string GetAttributes()
{
var type = this.GetType();
var returnValue = "";
var properties = type.GetProperties();
foreach (var propertyInfo in properties)
{
// Look at properties of the car
if (propertyInfo.Name.Contains("_") && propertyInfo.PropertyType.IsValueType &&
!propertyInfo.PropertyType.IsPrimitive)
{
var propValue = propertyInfo.GetValue(this);
var propType = propValue.GetType();
var propProperties = propType.GetProperties();
foreach (var propPropertyInfo in propProperties)
{
// Now get the properties of tire
// Here I just concatenate to a string - you can tweak this
returnValue += propPropertyInfo.GetValue(propValue).ToString();
}
}
}
return returnValue;
}
}
public struct tire
{
public int id { get; set; }
}
public class Program
{
static void Main(string[] args)
{
var mynewtire = new tire()
{
id = 5
};
var mynewcar = new car()
{
_id = mynewtire
};
Console.WriteLine(mynewcar.GetAttributes());
Console.ReadLine();
}
}
}
How can I access the custom attribute of the parent or owner object.
Look at the FieldInfo property of the SQLFieldInfo struct
Here's a more detailed program that will compile and run that shows what I need.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Employee myclass = new Employee();
// Load from sql server...
myclass.Name = "Alain";
myclass.Age = 51;
//----
MessageBox.Show(myclass.Name.ToString()); // Should return Alain
MessageBox.Show(myclass.Age.FieldInfo.Type.ToString()); // Should output "int"
}
}
// This next class is generated by a helper exe that reads SQL table design and create the class from it
[SQLTableAttribute(DatabaseName = "Employees", Schema = "dbo", TableName = "Employees")]
public class Employee
{
[SQLFieldAttribute(FieldName = "ID", Type = SqlDbType.Int)]
public SQLFieldInfo<int> ID { get; set; }
[SQLFieldAttribute(FieldName = "Name", Type = SqlDbType.NVarChar, Size = 200)]
public SQLFieldInfo<String> Name { get; set; }
[SQLFieldAttribute(FieldName = "Age", Type = SqlDbType.Int)]
public SQLFieldInfo<int> Age { get; set; }
}
public struct SQLFieldInfo<T>
{
private readonly T value;
public SQLFieldInfo(T Value)
{
this.value = Value;
}
public static implicit operator SQLFieldInfo<T>(T Value)
{
return new SQLFieldInfo<T>(Value);
}
public T Value
{
get
{
return this.value;
}
}
public override string ToString()
{
return this.value.ToString();
}
public SQLFieldAttribute FieldInfo
{
get
{
// Need to retreive the attribute class of the parent or declaring member
return null;
}
}
}
// Holds the sql field information
public class SQLFieldAttribute : Attribute
{
public string FieldName { get; set; }
public SqlDbType Type { get; set; }
public bool AllowNull { get; set; }
public int Size { get; set; }
}
// Holds the sql table information
public class SQLTableAttribute : Attribute
{
public string DatabaseName { get; set; }
public string Schema { get; set; } = "dbo";
public string TableName { get; set; }
}
Thank you!
Alain
My data class is as follows (should be fairly translatable to A above):
public class Foo
{
[Argument(Help = "Name", AssignmentDelimiter = "=")]
public string Name
{
get;
set;
}
}
A helper class is responsible of reading attribute values of objects:
static public string GetCommandLineDelimiter<T>(Expression<Func<T>> property)
{
if(property != null)
{
var memberExpression = (MemberExpression)property.Body;
string propertyName = memberExpression.Member.Name;
PropertyInfo prop = typeof(Arguments).GetProperty(propertyName);
if(prop != null)
{
object[] dbFieldAtts = prop.GetCustomAttributes(typeof(ArgumentAttribute), true);
if(dbFieldAtts.Length > 0)
{
return ((ArgumentAttribute)dbFieldAtts[0]).AssignmentDelimiter;
}
}
}
return null;
}
To use it, simply:
string delimiter = GetCommandLineDelimiter(() => myObject.Name);
That will get the attribute value of AssignmentDelimiter on property Name, i.e. "=".
First, MSDN is your friend.
Then, if you want to get the attributes for ancestors just specify true in the inherit flag of the method:
var attribute = typeof(A).GetProperty("myprop").GetCustomAttributes(true)
.OfType<MycustomAttrib>().FirstOrDefault();
This works. I am doing a lazy initialization of a reference to the custom attribute by using reflection to look at all the properties of all the types.
public class MycustomAttribAttribute : Attribute
{
public MycustomAttribAttribute(string name)
{
this.Name=name;
}
public string Name { get; private set; }
}
class A
{
public A() { MyProp=new B(); }
[MycustomAttrib(name: "OK")]
public B MyProp { get; set; }
}
class B
{
private static Lazy<MycustomAttribAttribute> att = new Lazy<MycustomAttribAttribute>(() =>
{
var types = System.Reflection.Assembly.GetExecutingAssembly().DefinedTypes;
foreach(var item in types)
{
foreach(var prop in item.DeclaredProperties)
{
var attr = prop.GetCustomAttributes(typeof(MycustomAttribAttribute), false);
if(attr.Length>0)
{
return attr[0] as MycustomAttribAttribute;
}
}
}
return null;
});
public string MyProp2
{
get
{
return att.Value.Name;
}
}
}
class Program
{
static void Main(string[] args)
{
// Finds the attribute reference and returns "OK"
string name = (new A()).MyProp.MyProp2;
// Uses the stored attribute reference to return "OK"
string name2 = (new A()).MyProp.MyProp2;
}
}
Is it possible to serialize static properties with JSON.NET without adding [JsonProperty] attribute to each property.
Example class:
public class Settings
{
public static int IntSetting { get; set; }
public static string StrSetting { get; set; }
static Settings()
{
IntSetting = 5;
StrSetting = "Test str";
}
}
Expected result:
{
"IntSetting": 5,
"StrSetting": "Test str"
}
Default behavior skips static properties:
var x = JsonConvert.SerializeObject(new Settings(), Formatting.Indented);
You can do this with a custom contract resolver. Specifically you need to subclass DefaultContractResolver and override the GetSerializableMembers function:
public class StaticPropertyContractResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var baseMembers = base.GetSerializableMembers(objectType);
PropertyInfo[] staticMembers =
objectType.GetProperties(BindingFlags.Static | BindingFlags.Public);
baseMembers.AddRange(staticMembers);
return baseMembers;
}
}
Here all we're doing is calling the base implementation of GetSerializableMembers, then adding public static properties to our list of members to serialize.
To use it you can create a new JsonSerializerSettings object and set the ContractResolver to an instance of the StaticPropertyContractResolver:
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = new StaticPropertyContractResolver();
Now, pass those settings to JsonConvert.SerializeObject and everything should work:
string json = JsonConvert.SerializeObject(new Settings(), serializerSettings);
Output:
{
"IntSetting": 5,
"StrSetting": "Test str"
}
Example: https://dotnetfiddle.net/pswTJW
A more complicated way to solve this:
Solution 1:
public class Settings
{
int intsetting { get; set; } /*= 0;*/ // commented only allowed in C# 6+
string strsetting { get; set; } /*= "";*/
public int IntSetting { get { return intsetting; } set { intsetting = value; } }
public string StrSetting { get { return strsetting; } set { strsetting = value; } }
static Settings()
{
IntSetting = 5;
StrSetting = "Test str";
}
}
Solution 2: (less complicated)
public class Settings
{
[JsonProperty]
public static int IntSetting { get; set; }
[JsonProperty]
public static string StrSetting { get; set; }
static Settings()
{
IntSetting = 5;
StrSetting = "Test str";
}
}
Adding the [JsonProperty] to all variables would be the easyest way of solving this, but when you don't want to use it Solution 1 would fit best for you.