Get element name from child class - c#

When generating an XML from this code:
internal class Program
{
public static void Main(string[] args)
{
using (StreamWriter myWriter = new StreamWriter(#"C:\Users\tomas\Documents\foo.xml", false))
{
var myFoo = new Foo();
myFoo.Bar = new BarChildOne();
XmlSerializer mySerializer = new XmlSerializer(typeof(Foo));
mySerializer.Serialize(myWriter, myFoo);
}
}
}
public class Foo
{
public BarBase Bar { get; set; }
}
[XmlInclude(typeof(BarChildOne))]
[XmlInclude(typeof(BarChildTwo))]
public abstract class BarBase
{
public string Name { get; set; }
}
[XmlRoot(ElementName = "BarChildOne")]
public class BarChildOne : BarBase
{
public BarChildOne()
{
this.Name = "BarChildOne";
}
}
[XmlRoot(ElementName = "BarChildTwo")]
public class BarChildTwo : BarBase
{
public BarChildTwo()
{
this.Name = "BarChildTwo";
}
}
An XML like this is created:
<?xml version="1.0" encoding="utf-8"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Bar xsi:type="BarChildOne">
<Name>BarChildOne</Name>
</Bar>
</Foo>
However, I want the element name to be BarChildOne, as defined by in the child class, and not Bar.
Current: <Bar xsi:type="BarChildOne">
Expected: <BarChildOne>
If the derived class is BarChildTwo then the expected name is <BarChildTwo>.
Is this possible with the built-in XML serializer?

Update the Variable name inside Foo:
public class Foo
{
public BarBase BarChildOne { get; set; }
}
and update the referenced variable's name inside the serialization:
using (StreamWriter myWriter = new StreamWriter(#"C:\temp\foo.xml", false))
{
var myFoo = new Foo();
myFoo.BarChildOne/**/ = new BarChildOne();
XmlSerializer mySerializer = new XmlSerializer(typeof(Foo));
mySerializer.Serialize(myWriter, myFoo);
}
yields the requested result:
I want the element name to be BarChildOne, as defined in the child class, and not Bar.
<?xml version="1.0" encoding="utf-8"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<BarChildOne xsi:type="BarChildOne">
<Name>BarChildOne</Name>
</BarChildOne>
</Foo>

Related

How to pass array of IXmlSerializable class as parameter to WCF?

I have two classes, one implements IXmlSerializable and one has DataContract attribute:
public class Foo : IXmlSerializable
{
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
XElement element = (XElement)XNode.ReadFrom(reader);
if (element.Element("Foo") != null)
element = element.Element("Foo");
Property = Convert.ToInt32(element.Element("Property").Value);
}
public void WriteXml(XmlWriter writer)
{
var element = new XElement("Foo");
element.Add(new XElement("Property", Property));
element.WriteTo(writer);
}
public int Property { get; set; }
}
[DataContract]
public class Bar
{
[DataMember]
public int Property { get; set; }
}
Then I have the service interface
[ServiceContract]
public interface IFooBarService
{
[OperationContract]
void TestFoo(Foo toTest);
[OperationContract]
void TestListFoo(Foo[] toTest);
[OperationContract]
void TestBar(Bar toTest);
[OperationContract]
void TestListBar(Bar[] toTest);
}
And its implementation as:
public class FooBarService : IFooBarService
{
public void TestFoo(Foo toTest)
{
var a = toTest.Property;
}
public void TestListFoo(Foo[] toTest)
{
foreach (var item in toTest)
{
var x = item.Property;
}
}
public void TestBar(Bar toTest)
{
var a = toTest.Property;
}
public void TestListBar(Bar[] toTest)
{
foreach (var item in toTest)
{
var x = item.Property;
}
}
}
SOAP UI generated the xml needed to call the service. All works correctly except the call to TestListFoo where I receive an empty array
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:com="http://schemas.datacontract.org/2004/07/Com.Panotec.Remote.Core.WebService" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<soapenv:Header/>
<soapenv:Body>
<tem:TestListFoo>
<tem:toTest>
<Foo>
<Property>1</Property>
</Foo>
<Foo>
<Property>1</Property>
</Foo>
<Foo>
<Property>1</Property>
</Foo>
<Foo>
<Property>1</Property>
</Foo>
</tem:toTest>
</tem:TestListFoo>
</soapenv:Body>
</soapenv:Envelope>
What am I missing? Is it possible to achieve what I need?
If not, how can I add the DataContract attribute to the class that implements IXmlSerializable?
Thanks

How to serialize attribute on a property or skip a class hierarchy

I have a class that I am serializing using XmlSerializer. In addition to other properties, I need to add a chunk of pre-built xml to the object. I already asked how to handle that chuck of xml in this post: How to remove empty namespace attribute on manually added xml string when serializing object?
Now I need to add an attribute to the property that contains the xml string. I understand how to add an attribute to a class but not to a property. If I create a new class to hold the attribute, I get an extra hierarchy in my output.
Here is my simplified code:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public XmlElement Extension { get; set; }
public Book()
{
}
public void AddExtension()
{
string xmlString = "<AdditionalInfo xmlns=\"http://www.somenamespace.com\">" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";
this.Extension = GetElement(xmlString);
}
public static XmlElement GetElement(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
}
static void Main(string[] args)
{
TestSerialization p = new TestSerialization();
Book bookOne = new Book();
bookOne.Title = "How to Fix Code";
bookOne.Author = "Dee Bugger";
bookOne.AddExtension();
System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(Book), "http://www.somenamespace.com");
using (var writer = new StreamWriter("C:\\BookReport.xml"))
using (var xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings { Indent = true }))
{
serializer.Serialize(xmlWriter, bookOne);
}
}
It generates the following output:
<?xml version="1.0" encoding="utf-8"?>
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.somenamespace.com">
<Title>How to Fix Code</Title>
<Author>Dee Bugger</Author>
<Extension>
<AdditionalInfo xmlns="http://www.somenamespace.com">
<SpecialHandling>Some Value</SpecialHandling>
</AdditionalInfo>
</Extension>
</Book>
Now I need to add an attribute to Extension to make the Extension output look like:
...
<Extension Modifier="ABC">
<AdditionalInfo xmlns="http://www.somenamespace.com">
<SpecialHandling>Some Value</SpecialHandling>
</AdditionalInfo>
</Extension>
Is there a way to change the Book class to do this? I tried to create an Extension class to hold the Modifier attribute and the XmlElement of the xml string but that resulted in an extra level:
public class Extension
{
[XmlAttribute]
public string Modifier { get; set; }
[XmlElementAttribute("Extension")]
public XmlElement ExtensionAsElement { get; set; }
public Extension()
{
}
public Extension(XmlElement extensionAsElement)
{
this.Modifier = "ABC";
this.ExtensionAsElement = extensionAsElement;
}
}
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
[XmlElementAttribute("Extension")]
public Extension ExtensionObj { get; set; }
public Book()
{
}
public void AddExtension()
{
string xmlString = "<AdditionalInfo xmlns=\"http://www.somenamespace.com\">" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";
this.ExtensionObj = new Extension(GetElement(xmlString));
}
public static XmlElement GetElement(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
}
<?xml version="1.0" encoding="utf-8"?>
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.somenamespace.com">
<Title>How to Fix Code</Title>
<Author>Dee Bugger</Author>
<Extension Modifier="ABC">
<Extension>
<AdditionalInfo xmlns="http://www.somenamespace.com">
<SpecialHandling>Some Value</SpecialHandling>
</AdditionalInfo>
</Extension>
</Extension>
</Book>
Note: this is an overly simplified example of my code, the Book class is not my root. I only need to serialize, not deserialize.
You can use [XmlAnyElement("Extension")] to specify that your Extension property should be inserted as-is into the XML stream as the element <Extension> itself, rather than as a child of an element of that name. Having done so, you will be able to set attributes on the element itself using SetAttribute() and GetAttribute().
Thus your Book class becomes something like:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
[XmlAnyElement("Extension")]
public XmlElement Extension { get; set; }
public Book()
{
this.Extension = new XmlDocument().CreateElement("Extension");
}
public Book AddExtension()
{
string innerXmlString = "<AdditionalInfo xmlns=\"http://www.somenamespace.com\">" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";
if (Extension == null)
// Since Extension is marked with [XmlAnyElement("Extension")], its value must
// be an XmlElement named "Extension". Its InnerXml can be anything.
Extension = new XmlDocument().CreateElement("Extension");
Extension.InnerXml = innerXmlString;
return this;
}
const string ModifierName = "Modifier";
[XmlIgnore]
public string Modifier
{
get
{
if (Extension == null)
return null;
return Extension.GetAttribute(ModifierName);
}
set
{
if (Extension == null)
AddExtension();
if (value == null)
Extension.RemoveAttribute(ModifierName);
else
Extension.SetAttribute(ModifierName, value);
}
}
}
And creating XML from the following:
var bookOne = new Book { Title = "How to Fix Code", Author = "Dee Bugger", Modifier = "AAA" }
.AddExtension();
Produces the result:
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Title>How to Fix Code</Title>
<Author>Dee Bugger</Author>
<Extension Modifier="AAA">
<AdditionalInfo xmlns="http://www.somenamespace.com">
<SpecialHandling>Some Value</SpecialHandling>
</AdditionalInfo>
</Extension>
</Book>

Can I specify the element name of the items when I inherit from List<T>

I am trying to figure out a way to specify the element name for serialization when I inherit from List
class
{
[XmlRoot(ElementName = "Foo")]
public class Widget
{
public int ID { get; set; }
}
[XmlRoot(ElementName = "Foos")]
public class WidgetList : List<Widget>
{
}
}
public static XElement GetXElement(object obj)
{
using (var memoryStream = new MemoryStream())
{
using (TextWriter streamWriter = new StreamWriter(memoryStream))
{
var xmlSerializer = new XmlSerializer(obj.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("dn", "http://defaultnamespace");
xmlSerializer.Serialize(streamWriter, obj, ns);
return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
}
}
static void Main(string[] args)
{
WidgetList list = new WidgetList();
list.Add(new Widget { ID = 0 });
list.Add(new Widget { ID = 1 });
XElement listElement = GetXElement(list);
Console.WriteLine(listElement.ToString());
Console.ReadKey();
}
Result:
<Foos xmlns:dn="http://defaultnamespace">
<Widget>
<ID>0</ID>
</Widget>
<Widget>
<ID>1</ID>
</Widget>
</Foos>
Desired Result:
<Foos xmlns:dn="http://defaultnamespace">
<Foo>
<ID>0</ID>
</Foo>
<Foo>
<ID>1</ID>
</Foo>
</Foos>
I was mostly wondering if I could modify "GetXElement" to respect the XmlRoot attribute of "Widget", but I am open to other ideas as long as I can still inherit from list. I do not like the solution given here: Serialize a generic collection specifying element names for items in the collection
[XmlType(TypeName = "Foo")]
[Serializable]
public class Widget
{
public int ID { get; set; }
}

Exclude some type from XMLSerializer

I have a class has a property need to be serialized with some specified type.
[Serializable]
public class MyClass
{
private object _data;
//[Any magic attribute here to exclude some types while serialization?]
public object Data
{
get { return _data;}
set { _data = value; }
}
}
[Serializable]
public class A
{}
[Serializable]
public class B
{}
MyClass myClass = new MyClass();
In some cases I have:
myClass.Data = A;
In some cases I have:
myClass.Data = B;
And then I do serialization for MyClass.
My question is: how can I serialize class B but class A as Data property inside MyClass?
Thank you for helping me!
You are looking for the ShouldSerialize Pattern:
[XmlRoot(ElementName="Foo")]
public class Foo
{
[XmlElement(ElementName="Bar")]
public int Bar { get; set; }
public bool ShouldSerializeBar()
{
return (Bar > 10);
}
}
The property 'Bar' will be serialized if it's greater than 10, otherwise, it won't.
Just create a boolean method with 'ShouldSerialize' in front of your property name. If the boolean then returns false, the property wil not be serialized.
More specific to your situation:
[XmlInclude(typeof(Foo))]
[XmlInclude(typeof(Bar))]
[XmlRoot(ElementName = "Foo")]
public class FooBar
{
[XmlElement(ElementName = "Data")]
public object Data { get; set; }
public bool ShouldSerializeData()
{
return (Data.GetType() == typeof(Foo));
}
}
[XmlRoot(ElementName="Foo")]
public class Foo
{
[XmlElement(ElementName="Bar")]
public int Bar { get; set; }
}
[XmlRoot(ElementName = "Bar")]
public class Bar
{
[XmlElement(ElementName = "Foo")]
public int Foo { get; set; }
}
Try
[XmlIgnore] attribute class
namespace System.Xml.Serialization
use XmlSerializer Class to serialize
I think that you are looking for this - http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlattributes.xmlignore(v=vs.110).aspx
You can add [XmlIgnoreAttribute] attributte to your Data property, and optionally (if (Data is B) ..) remove it with newly created XmlAttributes. Then you use new XmlSerializer with edited behaviour.
Edit: Example:
public class A
{
public string Text { get; set; }
}
public class B : A { }
public class C : A { }
[XmlInclude(typeof(B))]
[XmlInclude(typeof(C))]
public class D
{
public object Data { get; set; }
public string Test = "Test";
}
class Program
{
static void Main(string[] args)
{
var d = new D();
d.Data = new B() { Text = "Data" };
var xSer = CreateOverrider(d);
xSer.Serialize(new StreamWriter(File.OpenWrite("D:\\testB.xml")), d);
}
public static XmlSerializer CreateOverrider(D d)
{
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes();
attrs.XmlIgnore = d.Data is B;
xOver.Add(typeof(D), "Data", attrs);
XmlSerializer xSer = new XmlSerializer(typeof(D), xOver);
return xSer;
}
}
For B you get:
<?xml version="1.0" encoding="utf-8"?>
<D xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Test>Test</Test>
</D>
For C:
<?xml version="1.0" encoding="utf-8"?>
<D xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Test>Test</Test>
<Data xsi:type="C">
<Text>Data</Text>
</Data>
</D>

Can I flatten a referenced object to XML using attributes?

If I have the following:
public class A
{
public B b {get;set;}
}
public class B
{
public string Name {get;set;}
public string Address {get;set;
}
what I want is the xml as:
<A Name="some data" Address="address..." />
So I am trying to flatten my referenced object as attributes.
Is this possible with an XmlSerializer?
yeah, you can do this by using the IXmlSerializable interface:
[Serializable]
public class MyClass : IXmlSerializable
{
public MySubClass SubClass { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartAttribute("Name");
writer.WriteString(SubClass.Name);
writer.WriteEndAttribute();
writer.WriteStartAttribute("Phone");
writer.WriteString(SubClass.Phone);
writer.WriteEndAttribute();
}
}
[Serializable]
public class MySubClass
{
public string Name { get; set; }
public string Phone { get; set; }
}
and then call it like this
var serializer = new XmlSerializer(typeof(MyClass));
using (var writer = new StringWriter())
{
var myClass = new MyClass() {SubClass = new MySubClass() {Name = "Test", Phone = "1234"}};
serializer.Serialize(writer, myClass);
string xml = writer.ToString();
}
this is the xml result:
<?xml version="1.0" encoding="utf-16"?>
<MyClass Name="Test" Phone="1234" />
see msdn too: http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
or you could just specify the attributes that #Morpheus named ;)

Categories

Resources