Is it possible to hide an inherited data member? - c#

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>

Related

XML Serialize elements inside one another

I have a class that could be serialized on its own, for example:
[XmlRoot("NameOfMyRoot", Namespace = "myNamespace")]
public class Inner
{
public Inner(){}
public string SomeString { get; set; }
}
Which is perfectly serializing into (I am using ns alias for myNamespace, see full demo)
<?xml version="1.0" encoding="utf-16"?>
<NameOfMyRoot xmlns:ns="myNamespace">
<ns:SomeString></ns:SomeString>
</NameOfMyRoot>
Now I want this object to be part of another one (the wrapper):
[XmlRoot("Root")]
public class Outer<T>
{
public T Property{ get; set; }
public Outer(){}
public Outer(T inner)
{
Property = inner;
}
}
Which gives me this:
<?xml version="1.0" encoding="utf-16"?>
<Root xmlns:ns="myNamespace">
<Property>
<ns:SomeString></ns:SomeString>
</Property>
</Root>
What I want is just embedding inner object as-is into its parent, like so:
<?xml version="1.0" encoding="utf-16"?>
<Root>
<NameOfMyRoot xmlns:ns="myNamespace">
<ns:SomeString></ns:SomeString>
</NameOfMyRoot>
</Root>
Notice that namespace should not move to root, and I can't specify element's name, since there will be many different types.
Fiddle with full example.
Of cource, I can just serialize them separately and combine through some nasty string manipulation, but I hope there is a neat way to achieve this somehow.
There's no easy way to do that. You can do it at runtime, though:
// perhaps discover these details at runtime
var attribs = new XmlAttributeOverrides();
attribs.Add(typeof(Outer<Inner>), "Property", new XmlAttributes
{
XmlElements = { new XmlElementAttribute {ElementName = "NameOfMyRoot" } }
});
attribs.Add(typeof(Inner), "SomeString", new XmlAttributes
{
XmlElements = { new XmlElementAttribute { Namespace = "myNamespace"} }
});
var ser = new XmlSerializer(typeof(Outer<Inner>), attribs);
var obj = new Outer<Inner> { Property = new Inner { SomeString = "abc" } };
ser.Serialize(Console.Out, obj);
Note that you must store and re-use such a serializer; for simple new XmlSerializer() examples, the serializer is cached internally and re-used; however, for non-trivial cases like this, a new dynamic type is emitted each time, so you will leak memory (it won't be unloaded).
That gives you:
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<NameOfMyRoot>
<SomeString xmlns="myNamespace">abc</SomeString>
</NameOfMyRoot>
</Root>
If you don't want the two xmlns alias declaration, those can be removed separately, but they do not change the meaning, at least to a compliant reader. Likewise, the ns can be added as an alias:
var obj = new Outer<Inner> { Property = new Inner { SomeString = "abc" } };
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
ns.Add("ns", "myNamespace");
ser.Serialize(Console.Out, obj, ns);
which gives:
<Root xmlns:ns="myNamespace">
<NameOfMyRoot>
<ns:SomeString>abc</ns:SomeString>
</NameOfMyRoot>
</Root>
As an alternative approach:
using System;
using System.Xml;
using System.Xml.Serialization;
[XmlRoot("NameOfMyRoot")]
public class Inner
{
public Inner() { }
[XmlElement(Namespace = "myNamespace")]
public string SomeString { get; set; }
}
static class Program
{
static void Main()
{
using (XmlWriter writer = XmlWriter.Create(Console.Out))
{
writer.WriteStartDocument(false);
writer.WriteStartElement("Root");
var ser = new XmlSerializer(typeof(Inner));
var obj = new Inner { SomeString = "abc" };
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
ns.Add("ns", "myNamespace");
ser.Serialize(writer, obj, ns);
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
}
What I've done here is to manually write the root node, and only get the XmlSerializer to write the inner content. Note that I had to change the attributes aroung SomeString to make it work in the way you expect (where it is the string that has the namespace, not the object).

Deserialize a class with interface

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>

How to include attributes for an element (not the root) with XML Serialization in C#

I need to generate an XML that looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<inboundMessage xmlns="http://www.myurl.net">
<header>
<password>mypwd</password>
<subscriberId>myuser</subscriberId>
</header>
<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="myType">
<eventDate>2012-09-05T12:13:45.561-05:00</eventDate>
<externalEventId />
<externalId>SomeIdC</externalId>
</message>
</inboundMessage>
The problem is that I don't know how to include the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="myType" in my tag. My class that I need to serialize is this:
[XmlType("inboundMessage")]
[XmlRoot(Namespace = "http://www.myurl.net")]
public class InboundMessage
{
[XmlElement(ElementName = "header")]
public Header _header;
[XmlElement(ElementName = "message")]
public List<MyType> _messages;
}
What XmlAttributes do I need to add to my "_messages" member to make it serialize the way I want?
TIA,
Ed
Use XmlAttribute like this:
public class MyType
{
[XmlAttribute("type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type { get; set; }
}
A coleague of mine came up with a somewhat similar solution, yet more complete. MyType had two properties added:
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces { get; set; }
[XmlAttribute("type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type
{
get { return _type; }
set { _type = value; }
}
with _type being defined like this:
private string _type = "myType";
Then the serialization was done in a different way:
// declare an XmlAttributes object, indicating that the namespaces declaration should be kept
var atts = new XmlAttributes { Xmlns = true };
// declare an XmlAttributesOverrides object and ...
var xover = new XmlAttributeOverrides();
// ... add the XmlAttributes object with regard to the "Namespaces" property member of the "Message" type
xover.Add(typeof(MyType), "Namespaces", atts);
// set the Namespaces property for each message in the payload body
var messageNamespaces = new XmlSerializerNamespaces();
messageNamespaces.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
foreach (var message in myInboundMessage._messages)
{
message.Namespaces = messageNamespaces;
}
// create a serializer
var serializer = new XmlSerializer(object2Serialize.GetType(), xover);
// add the namespaces for the root element
var rootNamespaces = new XmlSerializerNamespaces();
rootNamespaces.Add("", "http://www.myurl.net");
// serialize and extract the XML as text
string objectAsXmlText;
using (var stream = new MemoryStream())
using (var xmlTextWriter = new XmlTextWriter(stream, null))
{
serializer.Serialize(xmlTextWriter, object2Serialize, rootNamespaces);
stream.Seek(0, SeekOrigin.Begin);
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
objectAsXmlText = Encoding.UTF8.GetString(buffer);
}
Finally, the InboundMessage was decorated like this:
[XmlRoot("inboundMessage", Namespace = "http://www.myurl.net")]
With this approach I get exactely what I need.

Custom XML-element name for base class field in serialization

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; }
}
}

Placing a Property into a Different XML Namespace with XML Serialization

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

Categories