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.
Related
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'm quite new to XML serialization/deserialization and I'm wondering how I should apply tags (XmlElement, XmlAttribute etc) to the following objects which I'm writing to XML files, to make it most optimal for me to later use LINQ or similar to get out the data I want. Let me give an example:
[XmlRoot("Root")]
public class RootObject
{
public Services Services { get; set; }
}
public class Services
{
public Service TileMapService { get; set; }
}
public class Service
{
public string Title { get; set; }
public string href { get; set; }
}
Here I've defined some properties which I'm going to write to XML with some values I'm gonna add later. At this point, I've hardcoded in the values in this method:
public static void RootCapabilities()
{
RootObject ro = new RootObject()
{
Services = new Services()
{
TileMapService = new Service()
{
Title = "Title",
href = "http://something"
}
}
};
Which gets me this XML output:
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<TileMapService>
<Title>Title</Title>
<href>http://something</href>
</TileMapService>
</Services>
</Root>
My question is if this is a valid way of doing it or if I have to use the 'XmlElement' and 'XmlAttribute' tags to later deserialize this XML file and get the information out of it, that I want (for example 'title').
I haven't been sure how to write this question, so please let me know if it's too vague.
I would not use XML serialization if I were you. Any changes to your schema/object structure and you immediately lose backwards compatibility due to how XML serialization works as a technology.
Rather separate out the XML serialization from the actual class structure - this way you can make changes to the XML/object schema and write proper migrating functions to handle any changes you make in the future.
Rather use the LINQ-to-XML classes to construct your XML document and save it as they are the easiest way in .NET to convert object structures to XML in a decoupled manner.
if the output is what you expect I think that you shouldn't change anything.
You should use Decorators like XmlAttribute or XmlIgnore when you want change the default behavior (e.g don't include a field, include one field as an attribute...)
Their role is obtain full control of the serialization and avoid unexpected behaviors
If you don't want to worry about the nitty-gritty of the serialization, XmlSerializer has always treated me well.
public static void SerializeObject(T obj, string file)
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextWriter w = new StreamWriter(file);
s.Serialize(w, obj);
w.Close();
}
public static T DeserializeObject(string file)
{
XmlSerializer s = new XmlSerializer(typeof(T));
using (StreamReader r = new StreamReader(file))
{
return (T)s.Deserialize(r);
}
}
This should really only be used with references types though (objects, not primitive types or structs). Deserialize can return null and casting that to a value type will bring nothing but heartache.
When I serialize the following class, the ContentPageId XML element is missing from the resulting XML file.
[CollectionDataContract(ItemName = "Widget")]
public sealed class StructurePage : List<Widget>, IEquatable<StructurePage>
{
[DataMember]
public int ContentPageId
{
get;
set;
}
public StructurePage(){}
public StructurePage(int pageId)
{
this.ContentPageId = pageId;
}
public bool Equals(StructurePage other)
{
return this.ContentPageId.Equals(other.ContentPageId);
}
}
Why is the property skipped when serializing and how to include it as XML element?
Is it possible to include it in serialization as an XML attribute to the StructurePage element? Was looking for this around the net but could find any info on it, apparently with XmlSerializer there was XmlAttributeAttribute attribute but no such thing with DataContractSerializer.
Go through this post http://social.msdn.microsoft.com/Forums/eu/wcf/thread/57eb195a-43a9-47fe-8b1a-a9d23feb2df2
According to this
Collection data contract classes cannot contain extra data members.
Hope this helps.
I am using the XmlSerializer, and was wondering if there is any way, using overrides or something to that effect to get the XmlSerializer to output the types of some nodes.
My problem is that I have serialized a byte array.
class MyClass {
public string Name { get; set; }
public byte[] Bytes { get; set; }
}
I am consuming the xml in a generic service.
The service collects the xml as .
<MyClass>
<Name>Test</Name>
<Bytes>U2NhcnkgQnVnZ2Vy</Bytes>
</MyClass>
Is there any way to either generate an xsd at runtime, or somehow output something like this.
I cannot change the class I am serializing, but I can apply overrides to the serializer or in some other way control the serialization.
<Bytes xsi:type='BinaryOfSomeKind'>BlahBlah</Bytes>
I need to know that the data is binary somehow.
Thanks
Craig.
If your class is supplied by a third party then you know your properties and property types and you can deduce your XML and XSD from it. You can create your XSD manually or with the help of a XML tool for example XMLSpy (not free BTW) or XMLFox which is free of charge.
If you know the xml is going to be in that format that you put in the question and you have your class ready you can decorate it as such for it to be deserialized.
The Deserialization class:
[XmlTypeAttribute]
[XmlRootAttribute("MyClass")]
public class MyClass
{
[XmlElementAttribute("Name")]
public string Name { get; set; }
[XmlElementAttribute("Bytes")]
public byte[] Bytes { get; set; }
}
The Deserialzation Method
public static object Deserialize(string xml)
{
var deserializer = new System.Xml.Serialization.XmlSerializer(typeof(MyClass));
using (var reader = XmlReader.Create(new StringReader(xml)))
{
return (MyClass)deserializer.Deserialize(reader);
}
}
The Main Method
static void Main(string[] args)
{
string xml = #"<MyClass>
<Name>Test</Name>
<Bytes>U2NhcnkgQnVnZ2Vy</Bytes>
</MyClass>";
MyClass obj = (MyClass)Deserialize(xml);
Console.ReadKey();
}
Make sure to add the following using statements:
using System.Xml.Serialization;
using System.Xml;
It deserialized it into an obj with "Test" as the byte array.
If you generate the XSD at run time, then theres no way you can know what properties there are and it would be down to using reflection to test for specific properties, and then subsequently finding out what types they may be, is this what your after?