I have the following business object
public class Employee
{
[JsonProperty("first_name")]
public string FirstName { get; set; }
public string Lastname { get; set; }
public DateTime DateOfBirth { get; set; }
public uint Salary { get; set; }
}
And i have the following interface and implementation for json serialization
interface IJSONSerializer<T>
{
string Serialize(T obj);
}
class NewtonJsonSerialization<T> : IJSONSerializer<T>
{
public string Serialize(T obj)
{
return JsonConvert.SerializeObject(obj, Formatting.Indented);
}
}
And here is my client code
var Employee = new Employee();
Employee.FirstName = "Ihor";
Employee.Lastname = "fff";
IJSONSerializer<Employee> serailizer = new NewtonJsonSerialization<Employee>();
var result = serailizer.Serialize(Employee);
What i dislike here - that Employee knows that it will be serialized with some specific library(because of JsonProperty attribute). So, if i decide to use another json serializer or write my own, i will need to go through all business objects and modify them. Is it possible to make my business objects serialization ignorance ? If yes how ?
The same with XML serialization and XmlSerializer class: i need to mark properties in business object with some attribute.
There is a term "persistence ignorance" which means that business objects are not aware of persistence. Is there the same about serialization ?
Most serializers make use of .Net's DataContract attributes. Json.net does as well. Try this sample out and make sure to look up the documentation for DataMember on MSDN to learn how to influence serialization.
Unless you need serialized key/element names to different than defined on your C# class, you should not worry about attributes at all. Both JSON and XML work just fine without decorating you class properties with serialization specific attributes.
If you really need custom key/element names in serialized data, then you must decorate your class props with multiple attributes. Any serializer will just read relevant attribute and ignore rest.
public class Person
{
[JsonProperty("first_name")]
[XmlElement("first_name")]
public string FirstName { get; set; }
}
Calling Code
ISerializer<Person> serializer;
string result;
var person = new Person { FirstName = "Nikhil" };
serializer = new NewtonsoftJsonSerializer<Person>();
result = serializer.Serialize(person);
/* Output
{"first_name":"Nikhil"}
*/
serializer = new BuiltInXmlSerializer<Person>();
result = serializer.Serialize(person);
/* Output
<Person
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<first_name>Nikhil</first_name>
</Person>
*/
Edit:
As per #Michael Nero DataContract and DataMember attributes will work in most cases but not all. Very first exception on such list is .NET's built in XmlSerializer which does not work without XmlElement attribute for custom element name.
Related
I have a class which is decorated with XmlElement in a wrong way, but it has also attributes that could allow me to identify the fields I need.
I can only modify [IWantToSerializeThisAttribute] and add other attributes to MySerializableClass because any modification to property names or XmlElement names would involve heavy coding maintenance.
Here's how the class has been defined:
[XmlRoot("ARandomXmlRoot")]
public class MySerializableClass
{
//CAMPI DIR_DOCUMENTI
//[MetadatoDocumentoAlfresco] รจ un attributo che serve per selezionare i campi per l'aggiornamento dati massivo su alfresco
[IWantToSerializeThisAttribute]
[XmlElement("DocumentCode")]
public string DOC_CODE { get; set; }
[IWantToSerializeThisAttribute]
[XmlElement("DocumentId")]
public string DOC_ID { get; set; }
[XmlElement("DocumentCode")]
public string DOC_CODE_FOR_EMPLOYEES { get; set; }
[XmlElement("DocumentId")]
public string DOC_ID_FOR_EMPLOYEES { get; set; }
}
Now, if I do
XmlSerializer.Deserialize(xmlString, typeof(MySerializableClass));
I will get an error most probably because XmlSerializer is finding 2 times the
[XmlElement("DocumentCode")]
and sees it's a duplicate tag.
Anyway I have an
[IWantToSerializeThisAttribute]
that makes the 2 properties different.
Can I tell someway XmlSerializer.Deserialize to catch and valorize only the "IwantToSerializeThisAttribute" properties and ignore the others?
I cannot change serialization with XmlOverrideAttributes, but maybe there is some way to do it during deserialization.
Thanks everyone
Try with XmlOverrideAttributes and Reflection. Using LINQ just to make it short.
This worked for me:
string XmlString = "<ARandomXmlRoot> XML HERE </ARandomXmlRoot>";
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
//Select fields I DON'T WANT TO SERIALIZE because they throw exception
string[] properties = (new MySerializableClass())
.GetType().GetProperties()
.Where(p => !Attribute.IsDefined(p, typeof(IWantToSerializeThisAttribute)))
.Select(p => p.Name);
//Add an XmlIgnore attribute to them
properties.ToList().ForEach(field => overrides.Add(typeof(MySerializableClass), field, new XmlAttributes() { XmlIgnore = true }));
MySerializableClass doc = new MySerializableClass();
XmlSerializer serializerObj = new XmlSerializer(typeof(MySerializableClass), overrides);
using (StringReader reader = new StringReader(xmlString))
{
doc = (MySerializableClass)serializerObj.Deserialize(reader);
};
Cheers
Not sure if I understand correctly but let me try to give you some options:
Can I tell someway XmlSerializer.Deserialize to catch and valorize only the "IwantToSerializeThisAttribute" properties and ignore the others?
If you want to serialize only specific properties use [XmlIgnore] attribute for the properties you want to omit. But if for some reason (DOC_CODE_FOR_EMPLOYEES and DOC_ID_FOR_EMPLOYEES)
But if you mean that it should be only omitted while deserializing, but serialization should still be done for all properties I'd think about implementing IXmlSerializable. This way you can specifically provide how would you like to read/write your xml.
I have an old system which in a request info call returns xml with names that look like:
postalCodeField, firstNameField...
That same system then has a modify call which takes xml that looks like:
PostalCode, fistName, lastName....
Is there a way to build an object that would deserialize the request, yet serialize the xml output with different names?
Specifically:
public class RequestInfo
{
public string firstNameField{get;set;}
public string lastNameField{get;set;}
}
public class ModifyInfo
{
public string firstName{get;set;}
public string lastName{get;set;}
public ModifyInfo(){}
public ModifyInfo(RequestInfo ri)
{
firstName = ri.firstNameField
.....
}
}
Is there a way through say attributes to make these into the same object?
EDIT
Is there a way to have a single object that would accept one tag name on deserialize, then output a different name on serialize?
< myTagField /> (deserialize to) myObj.MyTag (serialize to) < MyTag />
It's important to note which actual serializer you're using. Every different serializer works differently.
I assume you're using the System.Xml.Serialization.XmlSerializer. If that is the case, then you want to use the attributes in the System.Xml.Serialization namespace such as XmlElementAttribute like so:
public class Person
{
[System.Xml.Serialization.XmlElement(ElementName = "firstNameField")]
public string FirstName { get; set; }
}
This assumes the field is an XML element. If it's an attribute, use the XmlAttribute attribute.
Check Attributes That Control XML Serialization on MSDN. You will need XmlElement for properties and, possibly, XmlRoot for root class.
If you need to override property names only during deserialization, than you can define attributes dynamically, using XmlAttributeOverrides:
public XmlSerializer CreateOverrider()
{
XmlElementAttribute xElement = new XmlElementAttribute();
xElement.ElementName = "firstName";
XmlAttributes xElements = new XmlAttributes();
xElements.XmlElements.Add(xElement);
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
xOver.Add(typeof(RequestInfo), "firstNameField", xElements);
XmlSerializer xSer = new XmlSerializer(typeof(RequestInfo), xOver);
return xSer;
}
I hope to find a solution from you. What I need is to serialize ValidatorList class object into an xml document. How to do this?
I tried like this:
XmlSerializer _serializer = new XmlSerializer(list);
But I don't know how to make output of xml for list that has several classes.
C#
_list= new ListVal();
Type _type = typeof(ras);
_list.Add(new RequiredField
{
Property = _type.GetProperty("CustRef")
}.Add(new AsciiVal()));
_list.Add(new RequiredField
{
Property = _type.GetProperty("ctr")
}.Add(new StringLengthVal
{
min= 3,
max= 3
}));
[Serializable]
public class Field
{
public Field Next
{
get;
set;
}
public Field TypeName
{
get;
set;
}
public Field PropertyName
{
get;
set;
}
}
public class RequiredField : Field
{
//TODO
}
public class AsciiVal: Field
{
//TODO
}
public class StringLengthVal: Field
{
//TODO
}
public class ListVal: List<Field>
{
//TODO
}
I have something close, but not exactly the Xml you want. In actual fact I think you'll see that the Xml produced below makes a bit more sense than what you have.
To get you started, you control the serialization and deserialization using attributes in the System.Xml.Serialization namespace. A few useful ones to read up on are
XmlRootAttribute
XmlElementAttribute
XmlAttributeAttribute
XmlIncludeAttribute
So I mocked up some code which closely matches your own. Notice the addition of some attributes to instruct the serializer how I want the Xml to be laid out.
[XmlInclude(typeof(AsciiValidator))]
[XmlInclude(typeof(RequiredValidator))]
[XmlInclude(typeof(StringLengthValidator))]
public class FieldValidator
{
[XmlElement("Next")]
public FieldValidator Next
{
get;
set;
}
[XmlElement("PropertyName")]
public string PropertyName
{
get;
set;
}
}
public class AsciiValidator: FieldValidator
{
}
public class RequiredValidator: FieldValidator
{
}
public class StringLengthValidator: FieldValidator
{
[XmlElement]
public int MinLength{get;set;}
[XmlElement]
public int MaxLength{get;set;}
}
[XmlRoot("ValidatorList")]
public class ValidatorList : List<FieldValidator>
{
}
Point of interest; Every class inheriting FieldValidator must be added to the list of known types using XmlIncludeAttribute so the serializer knows what to do with them)
Then I created an example object map:
var test = new ValidatorList();
test.Add(
new RequiredValidator()
{
PropertyName="CustRef",
Next = new AsciiValidator()
});
test.Add(
new RequiredValidator()
{
PropertyName="CurrencyIndicator",
Next = new StringLengthValidator(){
MinLength=3,
MaxLength = 10
}
});
Finally I told the serializer to serialize it (and output the result to the console)
var ser = new XmlSerializer(typeof(ValidatorList));
ser.Serialize(Console.Out,test);
This was the result:
<?xml version="1.0" encoding="utf-8"?>
<ValidatorList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="AsciiValidator" />
<PropertyName>CustRef</PropertyName>
</FieldValidator>
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="StringLengthValidator">
<MinLength>3</MinLength>
<MaxLength>10</MaxLength>
</Next>
<PropertyName>CurrencyIndicator</PropertyName>
</FieldValidator>
</ValidatorList>
Not a million miles away from what you wanted. There is the need to output certain things in a certain way (eg xsi:type tells the serializer how to deserialize back to the object map). I hope this gives you a good start.
Here is a live, working example: http://rextester.com/OXPOB95358
Deserialization can be done by calling the Deserialize method on the XmlSerializer.
For example, if your xml is in a string:
var ser = new XmlSerializer(typeof(ValidatorList));
var test = "<..../>" // Your Xml
var xmlReader = XmlReader.Create(new StringReader(test));
var validatorList = (ValidatorList)ser.Deserialize(xmlReader);
There are many overrides of Deserialize which take differing inputs depending if the data is in a Stream an existing reader, or saved to a file.
You have to decorate the class that contains the _validators field with the KonwnType attribute
[Serializable]
[KwownType(typeof(RequiredFieldValidator)]
[KwownType(typeof(AsciValidator)]
public class MySerialisableClass
I have several SO answers that detail how to serialize objects using XML. I'll provide links below.
However, since you're looking for a rather simple serialization of your object, you may want to read up on the DataContractSerializer. It's much less complicated than the old .NET 1.x XML Serialization.
Here's the list of SO answers:
Omitting all xsi and xsd namespaces when serializing an object in .NET?
XmlSerializer: remove unnecessary xsi and xsd namespaces
Suppress xsi:nil but still show Empty Element when Serializing in .Net
Using XmlAttributeOverrides on Nested Properties
Even though many of these have to do with XML serialization and namespaces, they contain complete examples of serializing an object to XML using .NET 1.x XML Serialization.
I'm attempting to write a set of classes to represent a particularly complex object, and in one of those classes, I have a property that is set as the base (abstract) class of three possible derived classes. I'm setting up an ASP.NET Web API to handle the serialization and deserialization, which means that, by default, it uses Json.NET for JSON. How can I get the Web API to properly deserialize JSON sent via POST or PUT into the proper derived class?
The class with the abstract member looks like this (I'm including the Xml decorators for clarity and because they work perfectly well for deserializing xml using the XmlSerializer)
[Serializable]
public class FormulaStructure {
[XmlElement("column", typeof(ColumnStructure))]
[XmlElement("function", typeof(FunctionStructure))]
[XmlElement("operand", typeof(OperandStructure))]
public AFormulaItemStructure FormulaItem;
}
The abstract class is pretty basic:
[Serializable]
public abstract class AFormulaItemStructure { }
And there are three derivatives of the abstract class:
[Serializable]
public class ColumnStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlAttribute("field")]
public string Field;
[XmlAttribute("display")]
public string Display;
}
[Serializable]
public class FunctionStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlAttribute("name")]
public string Name;
[XmlElement("parameters")]
public string Parameters;
}
[Serializable]
public class OperandStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlElement("left")]
public string Left;
[XmlElement("right")]
public string Right;
}
At present, using [DataContract] attributes, the Json.NET formatter fails to populate the derived class, leaving the property null.
Questions
Can I mix XmlSerializer attributes with DataContractSerializer attributes on the same class? I use the XmlSerializer because I use xml attributes in the xml I designed, but that can be changed if necessary since I am developing the xml schema myself.
What is the equivalent in Json.NET to [KnownType()] ? Json.NET doesn't appear to respect the DataContractSerializer version of KnownType. Will I need to roll my own JsonConverter to determine the proper type?
How would I decorate the classes so that DataContractSerializer or DataContractJsonSerializer will properly deserialize the objects in both Xml and Json? My goal is to put this into an ASP.NET Web API, so I want the flexibility to generate Xml or Json, as appropriate to the requested type. Is there an alternative formatter that I need to use to work with this complex class, if Json.NET won't work?
I need the ability to generate an object on the client side without necessarily including the .NET class names into the object.
Testing and Refinement
In my testing of the Web API, the default serialization sends down to the client:
{"FormulaItem":{"type":"int","field":"my_field","display":"My Field"}}
which is ideal for my purposes. Getting this to go back to the API and deserialize into the proper derived types, though, isn't working (it's generating null for the property).
Testing Tommy Grovnes answer below, the DataContractSerializer he used for testing generates:
{"FormulaItem":{"__type":"column:#ExpressionStructureExperimentation.Models","display":"My Field","field":"my_field","type":"int"}}
which doesn't work for me, or for code maintainability (refactoring becomes a PITA if I hard-code the entire namespace into the JavaScript for generating these objects).
You can mix as mentioned already but I don't think you need to, haven't used WEB api myself but WCF Rest produces xml and json from DataContracts (without Xml.. tags), tag your classes like this:
[DataContract]
public class FormulaStructure
{
[DataMember]
public AFormulaItemStructure FormulaItem;
}
[DataContract]
[KnownType(typeof(ColumnStructure))]
[KnownType(typeof(FunctionStructure))]
[KnownType(typeof(OperandStructure))]
public abstract class AFormulaItemStructure { }
[DataContract(Name="column")]
public class ColumnStructure : AFormulaItemStructure
{
[DataMember(Name="type")]
public string Type;
[DataMember(Name = "field")]
public string Field;
[DataMember(Name = "display")]
public string Display;
}
[DataContract(Name="function")]
public class FunctionStructure : AFormulaItemStructure
{
[DataMember(Name = "type")]
public string Type;
[DataMember(Name = "name")]
public string Name;
[DataMember(Name = "parameters")]
public string Parameters;
}
[DataContract(Name = "operand")]
public class OperandStructure : AFormulaItemStructure
{
[DataMember(Name = "type")]
public string Type;
[DataMember(Name = "left")]
public string Left;
[DataMember(Name = "right")]
public string Right;
}
If you need more control over the XML/JSON generated you might have to tweak this further. I used this code to test:
public static string Serialize(FormulaStructure structure)
{
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader reader = new StreamReader(memoryStream))
{
var serializer = new DataContractSerializer(typeof(FormulaStructure));
serializer.WriteObject(memoryStream, structure);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
public static FormulaStructure Deserialize(string xml)
{
using (Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
var deserializer = new DataContractSerializer(typeof(FormulaStructure));
return (FormulaStructure)deserializer.ReadObject(stream);
}
}
After we ran into some issues much further down the line with my previous answer, I discovered the SerializationBinder class that JSON can use for serializing/deserializing namespaces.
Code First
I generated a class to inherit the SerializationBinder:
public class KnownTypesBinder : System.Runtime.Serialization.SerializationBinder {
public KnownTypesBinder() {
KnownTypes = new List<Type>();
AliasedTypes = new Dictionary<string, Type>();
}
public IList<Type> KnownTypes { get; set; }
public IDictionary<string, Type> AliasedTypes { get; set; }
public override Type BindToType(string assemblyName, string typeName) {
if (AliasedTypes.ContainsKey(typeName)) { return AliasedTypes[typeName]; }
var type = KnownTypes.SingleOrDefault(t => t.Name == typeName);
if (type == null) {
type = Type.GetType(Assembly.CreateQualifiedName(assemblyName, typeName));
if (type == null) {
throw new InvalidCastException("Unknown type encountered while deserializing JSON. This can happen if class names have changed but the database or the JavaScript references the old class name.");
}
}
return type;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName) {
assemblyName = null;
typeName = serializedType.Name;
}
}
How it works
Let's say I have a set of classes defined thus:
public class Class1 {
public string Text { get; set; }
}
public class Class2 {
public int Value { get; set; }
}
public class MyClass {
public Class1 Text { get; set; }
public Class2 Value { get; set; }
}
Aliased Types
What this does is allows me to generate my own names for classes that will be serialized/deserialized. In my global.asax file, I apply the binder as such:
KnownTypesBinder binder = new KnownTypesBinder()
binder.AliasedTypes["Class1"] = typeof(Project1.Class1);
binder.AliasedTypes["WhateverStringIWant"] = typeof(Project1.Class2);
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.Binder = binder;
Now, whenever I serialize, say, MyClass as JSON, I get the following:
{
item: {
$type: "Project1.MyClass",
Text: {
$type: "Class1",
Text: "some value"
},
Value: {
$type: "WhateverStringIWant",
Value: 88
}
}
}
Known Types
I can also choose to strip off the assembly information and strictly use the class name by adding information to the KnownTypesBinder:
KnownTypesBinder binder = new KnownTypesBinder()
binder.KnownTypes.Add(typeof(Project1.Class1));
binder.KnownTypes.Add(typeof(Project1.Class1));
In the two examples given, Class1 is referenced the same way. However, if I refactor Class1 to, say, NewClass1, then this second example will start sending a different name. That may or may not be a big deal, depending on whether you are using the types or not.
Final Thoughts
The advantage of the AliasedTypes is that I can give it any string that I want, and it doesn't matter how much I refactor the code, the communication between the .NET and the JavaScript (or whatever consumer is out there) is unbroken.
Be careful not to mix AliasedTypes and KnownTypes that have the exact same class name, because the code is written that the AliasType will win out over KnownType. When the binder doesn't recognize a type (aliased or known), it will provide the full assembly name of the type.
In the end, I broke down and added the .NET class information to the module in string variables to make refactoring easier.
module.net = {};
module.net.classes = {};
module.net.classes['column'] = "ColumnStructure";
module.net.classes['function'] = "FunctionStructure";
module.net.classes['operand'] = "OperandStructure";
module.net.getAssembly = function (className) {
return "MyNamespace.Models." + module.net.classes[className] + ", MyAssembly";
}
and generated the JSON as
{
"FormulaItem": {
"$type": module.net.getAssembly('column'),
"type": "int",
"field": "my_field",
"display": "My Field"
}
}
I'm trying to serialize a class that derives from BindingList(Floor), where Floor is a simple class that only contains a property Floor.Height
Here's a simplified version of my class
[Serializable]
[XmlRoot(ElementName = "CustomBindingList")]
public class CustomBindingList:BindingList<Floor>
{
[XmlAttribute("publicField")]
public string publicField;
private string privateField;
[XmlAttribute("PublicProperty")]
public string PublicProperty
{
get { return privateField; }
set { privateField = value; }
}
}
I'll serialize an instance of CustomBindingList using the following code.
XmlSerializer ser = new XmlSerializer(typeof(CustomBindingList));
StringWriter sw = new StringWriter();
CustomBindingList cLIst = new CustomBindingList();
Floor fl;
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
ser.Serialize(sw, cLIst);
string testString = sw.ToString();
Yet testString above ends getting set to the following XML:
<CustomBindingList xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<Floor Height="10" />
<Floor Height="10" />
<Floor Height="10" />
</CustomBindingList>"
How do I get "publicField" or "publicProperty to serialize as well?
The short answer here is that .NET generally expects something to be a collection xor to have properties. This manifests in a couple of places:
xml serialization; properties of collections aren't serialized
data-binding; you can't data-bind to properties on collections, as it implicitly takes you to the first item instead
In the case of xml serialization, it makes sense if you consider that it might just be a SomeType[] at the client... where would the extra data go?
The common solution is to encapsulate a collection - i.e. rather than
public class MyType : List<MyItemType> // or BindingList<...>
{
public string Name {get;set;}
}
public class MyType
{
public string Name {get;set;}
public List<MyItemType> Items {get;set;} // or BindingList<...>
}
Normally I wouldn't have a set on a collection property, but XmlSerializer demands it...
XML serialization handles collections in a specific way, and never serializes the fields or properties of the collection, only the items.
You could either :
implement IXmlSerializable to generate and parse the XML yourself (but it's a lot of work)
wrap your BindingList in another class, in which you declare your custom fields (as suggested by speps)
This is known issue with XML serialization and inheriting from collections.
You can read more info on this here : http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/thread/0d94c4f8-767a-4d0f-8c95-f4797cd0ab8e
You could try something like this :
[Serializable]
[XmlRoot]
public class CustomBindingList
{
[XmlAttribute]
public string publicField;
private string privateField;
[XmlAttribute]
public string PublicProperty
{
get { return privateField; }
set { privateField = value; }
}
[XmlElement]
public BindingList<Floor> Floors = new BindingList<Floor>();
}
This means you can add floors by using Floors.Add and you will get the result you want, I hope, however, I didn't try it. Keep in mind that playing around with attributes is the key to XML serialization.