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;
}
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 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.
I need to generate an XML document that follows this specifictaion
<productName locale="en_GB">Name</productName>
but using XMLSeralization I am getting the following
<productName locale="en_GB">
<Name>Name</Name>
</productName>
My C# code is like this:
[Serializable]
public class productName
{
public productName()
{
}
public string Name;
[XmlAttribute]
public string locale;
}
XmlAttribute is what is required to show the locale in the correct place, but I am unable to figure out how to export the Name field correctly.
Does anyone have an idea?
Thanks
EDIT:
This is the code to generate the XML
public static class XMLSerialize
{
public static void SerializeToXml<T>(string file, T value)
{
var serializer = new XmlSerializer(typeof(T));
using (var writer = XmlWriter.Create(file))
serializer.Serialize(writer, value);
}
public static T DeserializeFromXML<T>(string file)
{
XmlSerializer deserializer = new XmlSerializer(typeof(T));
TextReader textReader = new StreamReader(file);
T result;
result = (T)deserializer.Deserialize(textReader);
textReader.Close();
return result;
}
}
Instead of specifying Name as element specify it as text value by adding [XmlText] attribute
[XmlText]
public string Value { get; set; }
This contains not only a direct answer to your question, but more of a indirect answer of how to solve similar issues like this in the future.
Start the other way around, with your xml, write your xml exactly like you want it and go from there, like this:
// assuming data.xml contains the xml as you'd like it
> xsd.exe data.xml // will generate data.xsd, ie xsd-descriptor
> xsd.exe data.xsd /classes // will generate data.cs, ie c# classes
> notepad.exe data.cs // have a look at data.cs with your favorite editor
Now just have a look at data.cs, this will contain an enormous amount of attributes and stuff and the namespaces are probably wrong, but at least you know how to solve your particular xml-issue.
The direct answer is to use the XmlTextAttribute on the given property, preferably named Value since that is the convention I've seen so far.
[Serializable]
public class productName {
public productName() { }
[XmlText]
public string Value {get; set;}
[XmlAttribute]
public string locale {get; set;}
}
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 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.