I have a class which contain an interface member variable.How can i deserialize this class
interface ISensor { }
[Serializable]
class Sensor: ISensor { }
[Serializable]
class Root
{
[XmlElement("Sensor")]
public List<ISensor> SensorList{ get; set; }
}
My XML will be like this
<?xml version="1.0" encoding="us-ascii"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Sensor >
<SensorName>Name1</SensorName>
<SensorValue>0.0</SensorValue>
</Sensor>
<Sensor>
<SensorName>Name2</SensorName>
<SensorValue>148.00</SensorValue>
</Sensor>
</Root>
Assuming you are using XmlSerializer, there are two changes required for both serialization and deserialization:
Tell the serializer the list of types that can appear, in other words the types that inherit from Sensor.
Use a class for the List rather than an interface, in other words replace List<ISensor> with List<Sensor>.
Unfortunately, the XmlSerializer does not handle interfaces in the way you want. See XmlSerializer serialize generic List of interface for more information.
If using a base class is not an option You could write your own XML serializer by implementing IXmlSerializable. Override ReadXml and parse the XML manually.
For example:
public interface ISensor { }
[Serializable]
public class Sensor : ISensor { }
[Serializable]
public class Root
{
// Changed List<ISensor> to List<Sensor>. I also changed
// XmlElement to XmlArray so it would appear around the list.
[XmlArray("Sensor")]
public List<Sensor> SensorList { get; set; }
}
[Serializable]
public class SensorA : Sensor
{
[XmlElement("A")]
public string A { get; set; }
}
[Serializable]
public class SensorB : Sensor
{
[XmlElement("B")]
public string B { get; set; }
}
class Program
{
public static void Main(string[] args)
{
XmlSerializer xmlSerializer;
Root root = new Root();
root.SensorList = new List<Sensor>();
root.SensorList.Add(new SensorA() {A = "foo"});
root.SensorList.Add(new SensorB() {B = "bar"});
// Tell the serializer about derived types
xmlSerializer = new XmlSerializer(typeof (Root),
new Type[]{typeof (SensorA), typeof(SensorB)});
StringBuilder stringBuilder = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(stringBuilder))
{
xmlSerializer.Serialize(stringWriter, root);
}
// Output the serialized XML
Console.WriteLine(stringBuilder.ToString());
Root root2;
using (StringReader stringReader = new StringReader(stringBuilder.ToString()))
{
root2 = (Root) xmlSerializer.Deserialize(stringReader);
}
}
}
The output from the Console.WriteLine statement is:
<?xml version="1.0" encoding="utf-16"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Sensor>
<Sensor xsi:type="SensorA">
<A>foo</A>
</Sensor>
<Sensor xsi:type="SensorB">
<B>bar</B>
</Sensor>
</Sensor>
</Root>
Related
I'm trying to loop into an XmlDocument to serialize objects. Let's suppose an simple xml :
<?xml version="1.0" encoding="iso-8859-15"?>
<root>
<message>
<id>1</id>
<text>test</text>
</message>
<message>
<id>2</id>
<text>test 2</text>
</message>
</root>
So this is my c# program :
class Program
{
static void Main(string[] args)
{
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreComments = true;
XmlSerializer serializer = new XmlSerializer(typeof(Message));
XmlReader xmlReader = XmlReader.Create(#"..\..\test.xml");
XmlDocument doc = new XmlDocument();
doc.Load(xmlReader);
foreach(XmlElement element in doc.DocumentElement.ChildNodes)
{
Console.WriteLine($"id : {element.SelectSingleNode("id").InnerText}, message : {element.SelectSingleNode("text").InnerText}");
Message message = (Message)serializer.Deserialize(XmlReader.Create(element.OuterXml.ToString()));
}
Console.ReadLine();
}
}
public class Message
{
public int id;
public string text;
}
but i got an error Illegal characters in path, but the print is okay, what's wrong ? and is there a way to serialize directly the XmlElement without go through the tostring() ?
Ok, i found the problem. The serializer is looking for a tag named Message like the name of the class but the tag was message with an lowercase m. It is still possible to differentiate the name of the class and the label of the tag and associate them with a decorator as :
[XmlRoot(ElementName = "message")]
public class Message
{
public int id { get; set; }
public string? text { get; set; }
}
My XML looks like this:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfAddressDetails xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AddressDetails>
<DbServerName>2k8</DbServerName>
</AddressDetails>
<AddressDetails>
<DbServerName>2k8R2D3</DbServerName>
</AddressDetails>
</ArrayOfAddressDetails>
And I created two classes for it as follows:
public class AddressDetails
{
public string DbServerName { get; set; }
}
}
and another class to hold a list of those:
public class AddressList
{
public List<AddressDetails> addressList= new List<AddressDetails>() ;
}
And this is how I am deserializng it:
XmlSerializer deSerializer = new XmlSerializer(typeof(AddressList));
TextReader reader = new StreamReader(#"C:\TEMP\MyXML.xml");
Object obj = deSerializer.Deserialize(reader);
AddressList adrsList = (AddressList)obj;
reader.Close();
But on Deserialize method I get this error:
You have to decorate:
AddressList class with XmlRoot attribute like this [XmlRoot("ArrayOfAddressDetails")]
addressList field with XmlElement attribute like this [XmlElement("AddressDetails")]
You have this at the end:
[XmlRoot("ArrayOfAddressDetails")]
public class AddressList
{
[XmlElement("AddressDetails")]
public List<AddressDetails> addressList = new List<AddressDetails>();
}
I have two classes. The base class has a public property with a DataMember. Now, I do not want this property to be serialized in the child class. I have no control over the parent class, and several other classes do inherit from it.
Is there a way to "hide" this attribute from the XmlSerializer that's used by WCF ? (the context here is a WCF RESTful web service).
Yes, it is possible using the XmlAttributeOverrides class, and the XmlIgnore property. Here is the example based on the one from MSDN:
public class GroupBase
{
public string GroupName;
public string Comment;
}
public class GroupDerived : GroupBase
{
}
public class Test
{
public static void Main()
{
Test t = new Test();
t.SerializeObject("IgnoreXml.xml");
}
public XmlSerializer CreateOverrider()
{
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes();
attrs.XmlIgnore = true; //Ignore the property on serialization
xOver.Add(typeof(GroupDerived), "Comment", attrs);
XmlSerializer xSer = new XmlSerializer(typeof(GroupDerived), xOver);
return xSer;
}
public void SerializeObject(string filename)
{
XmlSerializer xSer = CreateOverrider();
GroupDerived myGroup = new GroupDerived();
myGroup.GroupName = ".NET";
myGroup.Comment = "My Comment...";
TextWriter writer = new StreamWriter(filename);
xSer.Serialize(writer, myGroup);
writer.Close();
}
Result:
<?xml version="1.0" encoding="utf-8"?>
<GroupDerived xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GroupName>.NET</GroupName>
</GroupDerived>
Result with XmlIgnore = false;:
<?xml version="1.0" encoding="utf-8"?>
<GroupDerived xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GroupName>.NET</GroupName>
<Comment>My Comment...</Comment>
</GroupDerived>
How can I change XML-element name for field inherited from base class while doing serialization?
For example I have next base class:
public class One
{
public int OneField;
}
Serialization code:
static void Main()
{
One test = new One { OneField = 1 };
var serializer = new XmlSerializer(typeof (One));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
I get what I need:
<?xml version="1.0" encoding="utf-8"?>
<One xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
</One>
Now I have created new class inherited from A with additional field and custom XML element name for it:
public class Two : One
{
[XmlElement("SecondField")]
public int TwoField;
}
Serialization code:
static void Main()
{
Two test = new Two { OneField = 1, TwoField = 2 };
var serializer = new XmlSerializer(typeof (Two));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
As a result I get next output:
<?xml version="1.0" encoding="utf-8"?>
<Two xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
<SecondField>2</SecondField>
</Two>
The problem is that I want to change OneField in this output to FirstField without touching base class code (because I will use it too and the names must be original). How can I accomplish this?
Thank you.
Try this:
public class Two : One
{
private static XmlAttributeOverrides xmlOverrides;
public static XmlAttributeOverrides XmlOverrides
{
get
{
if (xmlOverrides == null)
{
xmlOverrides = new XmlAttributeOverrides();
var attr = new XmlAttributes();
attr.XmlElements.Add(new XmlElementAttribute("FirstField"));
xmlOverrides.Add(typeof(One), "OneField", attr);
}
return xmlOverrides;
}
}
[XmlElement("SecondField")]
public string TwoField;
}
And your serializer init is a lot easier:
var xmls = new System.Xml.Serialization.XmlSerializer(typeof(Two), Two.XmlOverrides);
Here's a workaround: Override the fields in the subclass and mark the overriden field with whatever name you need. For example,
class One
{
public int OneField { get; set; }
}
class Two : One
{
[XmlElement("FirstField")]
public new int OneField
{
get { return base.OneField; }
set { base.OneField = value; }
}
}
I am using VSTS2008 + C# + .Net 3.0. I am using below code to serialize XML, here is my current code and serialized XML file. My purpose is I want to make MyInnerObjectProperties belongs to a special XML namespace (http://foo/2009) and making this namespace as default namespace. Any ideas how to implement this?
Current output:
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<MyInnerObjectProperties>
<MyInnerObjectProperty>
<ObjectName>Foo Type</ObjectName>
</MyInnerObjectProperty>
<MyInnerObjectProperty>
<ObjectName>Goo Type</ObjectName>
</MyInnerObjectProperty>
</MyInnerObjectProperties>
</MyObjectProperty>
</MyClass>
Current code:
public class MyClass
{
private MyObject[] _myObjectProperty;
[XmlElement(IsNullable=false)]
public MyObject[] MyObjectProperty
{
get
{
return _myObjectProperty;
}
set
{
_myObjectProperty = value;
}
}
}
public class MyObject
{
private MyInnerObject[] _myInnerObjectProperty;
[XmlArrayItemAttribute("MyInnerObjectProperty", typeof(MyInnerObject), IsNullable=false)]
public MyInnerObject[] MyInnerObjectProperties
{
get
{
return _myInnerObjectProperty;
}
set
{
_myInnerObjectProperty = value;
}
}
}
public class MyInnerObject
{
public string ObjectName;
}
public class Program
{
static void Main(string[] args)
{
XmlSerializer s = new XmlSerializer(typeof(MyClass));
FileStream fs = new FileStream("foo.xml", FileMode.Create);
MyClass instance = new MyClass();
instance.MyObjectProperty = new MyObject[1];
instance.MyObjectProperty[0] = new MyObject();
instance.MyObjectProperty[0].MyInnerObjectProperties = new MyInnerObject[2];
instance.MyObjectProperty[0].MyInnerObjectProperties[0] = new MyInnerObject();
instance.MyObjectProperty[0].MyInnerObjectProperties[0].ObjectName = "Foo Type";
instance.MyObjectProperty[0].MyInnerObjectProperties[1] = new MyInnerObject();
instance.MyObjectProperty[0].MyInnerObjectProperties[1].ObjectName = "Goo Type";
s.Serialize(fs, instance);
return;
}
}
How about this:
[XmlArrayItemAttribute( Namespace = "http://foo.com/2009" /* other attr. params. */ )]
public MyInnerObject[] MyInnerObjectProperties
{
get { ... }
set { ... }
}
Try
public class MyObject
{
[XmlArrayItemAttribute("MyInnerObjectProperty", typeof (MyInnerObject),
IsNullable = false)]
[XmlArray(Namespace = "http://foo.com/2009")]
public MyInnerObject[] MyInnerObjectProperties { get; set; }
}
for me, this produces:
<?xml version="1.0" encoding="utf-8"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<MyInnerObjectProperties xmlns="http://foo.com/2009">
<MyInnerObjectProperty>
<ObjectName>Foo Type</ObjectName>
</MyInnerObjectProperty>
<MyInnerObjectProperty>
<ObjectName>Goo Type</ObjectName>
</MyInnerObjectProperty>
</MyInnerObjectProperties>
</MyObjectProperty>
</MyClass>
You need to create an XmlSerializerNamespaces object, and add your needed namespaces to it.
The XmlSerializerNamespaces object contains the XML namespaces and prefixes that the XmlSerializer uses to generate qualified names in an XML-document instance.
In your c# code:
XmlSerializerNamespaces myNameSpaces = new XmlSerializerNamespaces();
myNameSpaces.Add("MyInnerObject", "http://foo/2009");
Then, add an attribute to your class, like this:
public class MyInnerObject
{
[XmlElement(Namespace = "http://foo/2009")]
More info at:
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializernamespaces.aspx