Using XmlAttributeOverrides on Nested Properties - c#

I am trying to use XmlAttributeOverrides to control which class properties appear in the xml after the class has been serialized. It works on properties that are on the "root" class but not on nested properties. Here is a simple example to illustrate what I'm trying to accomplish.
My class hierarchy is as follows:
public class Main
{
public string Name { get; set; }
public Location Address { get; set; }
}
public class Location
{
public string StreetAddress { get; set; }
public Contact ContactInfo{ get; set; }
}
public class Contact
{
public string PhoneNumber { get; set; }
public string EmailAddr { get; set; }
}
When I serialize Main(), I get something like this:
<Main>
<Name></Name>
<Address>
<StreetAddress></StreetAddress>
<ContactInfo>
<PhoneNumber></PhoneNumber>
<EmailAddr></EmailAddr>
</ContactInfo>
</Address>
</Main>
What I am able to do is keep either Name or Address from appearing by using this:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("Address"));
overrides.Add(typeof(Main), "Address", attribs);
xs = new XmlSerializer(typeof(Main), overrides);
What I need to also be able to do is keep Main.Address.ContactInfo from being serialized SOMETIMES (if it's empty). I tried the following but they didn't work:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo "));
overrides.Add(typeof(Contact), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Contact), overrides);
and...
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo "));
overrides.Add(typeof(Main.Address.ContactInfo), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main.Address.ContactInfo), overrides);
I've actually tried a lot more, including using XPath statements to designate the attribute name to target but didn't want to fill this page up with failed attempts. Is what I'm asking even possible by this method?

For anyone else trying to do this with XmlAttributeOverrides, turns out that #user1437872 was very close to finding the answer. Here is the override code to ignore the nested element ContactInfo.
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo"));
overrides.Add(typeof(Address), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main), overrides);

There are easier ways to achieve what you're looking for.
You said that what you are trying to achieve is to not serialize /Main/Address/ContactInfo if ContactInfo contains no data.
If you leave your code as is, it will serialize all of Main's properties, whether they are null or empty or not. The first step, is you need to add a XmlSerializerNamespaces property to all of your objects or each empty object will be serialized as <myElement xsi:nil="true" />. This can be accomplished easily, as follows:
public MyXmlElement
{
public MyXmlElement()
{
// Add your own default namespace to your type to prevet xsi:* and xsd:*
// attributes from being generated.
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:myDefaultNamespace") });
}
[XmlElement("MyNullableProperty", IsNullable=false)]
public string MyNullableProperty
{
get
{
return string.IsNullOrWhiteSpace(this._myNullableProperty) ?
null : this._myNullableProperty;
}
set { this._myNullableProperty = value; }
}
[XmlNamespacesDeclaration]
public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } }
private XmlSerializerNamespaces _namespaces;
}
The code above declares a Namespaces property that holds all the relevant namespaces for the XML object. You should provide a default namespace for all of your objects (modeled after the code above). This prevents the xsi:* and xsd:* attributes from being output for your objects when they are serialized. Also, specify that the element is not nullable by using the System.Xml.Serialization.XmlElementAttribute.
Furthermore, by checking for string.IsNullOrWhiteSpace(someVariable) and returning null, then the
property will not be serialized when the above has been done.
So, putting this all together for your Location class:
public class Location
{
// You should have a public default constructor on all types for (de)sereialization.
public Location()
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:myNamespace"); // Default namespace -- prevents xsi:nil="true" from being generated, as well as xsd:* attributes.
});
}
public string StreetAddress
{
// If you don't want <StreetAddress xsi:nil="true" /> to be generated, do this:
get { return string.IsNullOrEmpty(this._streetAddress) ? null : this._streetAddress; }
// Otherwise, if you don't care, just do
// get;
// Only need to implement setter if you don't want xsi:nil="true" to be generated.
set { this._streetAddress = value; }
// Otherwise, just
// set;
}
private string _streetAddress;
[XmlElement("ContactInfo", IsNullable=false)]
public Contact ContactInfo
{
// You must definitely do this to prevent the output of ContactInfo element
// when it's null (i.e. contains no data)
get
{
if (this._contactInfo != null && string.IsNullOrWhiteSpace(this._contactInfo.PhoneNumber) && string.IsNullOrWhiteSpace(this._contactInfo.EmailAddr))
return null;
return this._contactInfo;
}
set { this._contactInfo = value; }
}
private Contact _contactInfo;
[XmlNamespacesDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
}
With these changes to your Location class, the empty ContactInfo property should no longer be serialized to XML when none of the properties are null, empty, or whitespace, or if ContactInfo itself is null.
You should make similar changes to your other objects.
See my other stackoverflow answers for more on .NET XML serialization:
XmlSerializer: remove unnecessary xsi and xsd namespaces
Omitting all xsi and xsd namespaces when serializing an object in .NET?
Suppress xsi:nil but still show Empty Element when Serializing in .Net

There is no need to add a ContactInfo element to the attribs
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
overrides.Add(typeof(Address), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main), overrides);

Related

How to set the namespace on serialization when property's namespace is empty?

I have 4 different namespaces and I add them as follows
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
namespaces.Add("abc", "urn:abc");
namespaces.Add(string.Empty, "urn:efg");
namespaces.Add("xyz", "urn:xyz");
My object looks like this (small example):
[XmlRoot(Namespace = "urn:abc")]
public class RootDocument
{
public Header Header { get; set; }
}
public class Header
{
//No namespace here
public SomeNode SomeNode { get; set; }
}
public class SomeNode
{
[XmlElement(Namespace = "urn:xyz")]
public OtherNode { get; set; }
}
public class OtherNode
{
[XmlText]
public string Value { get; set; }
}
The class SomeNode is an example of a class that has no namespace. I have over 50 classes with lots of properties and I want to avoid setting a namespace on each and every one of them.
When I serialize the above I get:
<?xml version="1.0" encoding="utf-16"?>
<abc:RootDocument xmlns="urn:efg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xyz="urn:xyz" xmlns:abc="urn:abc">
<abc:Header>
<abc:SomeNode>
<xyz:OtherNode>true</xyz:OtherNode>
</abc:SomeNode>
</abc:Header>
</abc:RootDocument
However, the output must be:
<?xml version="1.0" encoding="utf-16"?>
<abc:RootDocument xmlns:efg="urn:efg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xyz="urn:xyz" xmlns:abc="urn:abc">
<abc:Header>
<efg:SomeNode>
<xyz:OtherNode>true</xyz:OtherNode>
</efg:SomeNode>
</abc:Header>
</abc:RootDocument
Can I automatically set the namespace during serialization to efg on all nodes that have an empty workspace (ie. the properties don't have the Namespace of the XmlElement set)?
If you want to change the namespace on runtime you have the XmlAttributeOverrides:
The XmlAttributes object contains a union of attribute objects that
cause the XmlSerializer to override its default serialization behavior
for a set of objects. You choose the attribute objects to place in the
XmlAttributes object, depending on the particular behaviors you want
to override. For example, the XmlSerializer serializes a class member
as an XML element by default. If you want the member to be serialized
as an XM attribute instead, you would create an XmlAttributeAttribute,
assign it to the XmlAttribute property of an XmlAttributes, and add
the XmlAttributes object to the XmlAttributeOverrides object.
Use this overload to override an XmlRootAttribute or XmlTypeAttribute.
For example:
public class Group
{
public string GroupName;
[XmlAttribute]
public int GroupCode;
}
public class Sample
{
public XmlSerializer CreateOverrider()
{
// Create an XmlAttributeOverrides object.
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
/* Create an XmlAttributeAttribute to override the base class
object's XmlAttributeAttribute object. Give the overriding object
a new attribute name ("Code"). */
XmlAttributeAttribute xAtt = new XmlAttributeAttribute();
xAtt.AttributeName = "Code";
/* Create an instance of the XmlAttributes class and set the
XmlAttribute property to the XmlAttributeAttribute object. */
XmlAttributes attrs = new XmlAttributes();
attrs.XmlAttribute = xAtt;
/* Add the XmlAttributes object to the XmlAttributeOverrides
and specify the type and member name to override. */
xOver.Add(typeof(Group), "GroupCode", attrs);
XmlSerializer xSer = new XmlSerializer(typeof(Group), xOver);
return xSer;
}
}
And another one, with XmlSerializerNamespaces:
[XmlRoot("Node", Namespace="http://somenamepsace")]
public class MyType {
[XmlElement("childNode")]
public string Value { get; set; }
}
static class Program
{
static void Main()
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("myNamespace", "http://somenamepsace");
XmlSerializer xser = new XmlSerializer(typeof(MyType));
xser.Serialize(Console.Out, new MyType(), ns);
}
}

Serialise empty strings as nulls so they are not output

I've got an XSD which has been supplied by a third party and I've then converted this into classes using xsd.exe.
Due to the fact we do not own this XSD, I'm trying to not alter the generated classes in any way.
Without manually applying a blank string check to every single field we are mapping, e.g.:
!String.IsNullOrWhiteSpace(field) ? field : null;
I can't think of an easy way to serialise the blank strings in a way which means they are not output at all (no tag is produced).
The reason we cannot output blank strings is because the XSD has pattern constraints such as StringM50m1 which I believe means a minimum length of 1 is required (however, they are happy to not receive the tag if there is no content) which fail post-output validation with the following message:
The 'field' element is invalid - The value '' is invalid according to its datatype 'StringM50m1' - The Pattern constraint failed.
Any thoughts would be appreciated.
If you use XmlSerializer you can add XmlDefaultValue for required properties:
private static void Serialize()
{
XsdClass xc = new XsdClass();
xc.SomeString1 = string.Empty;
xc.SomeString2 = "value";
xc.SomeString3 = null;
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlDefaultValue = string.Empty;
foreach (var prop in TypeDescriptor.GetProperties(typeof(XsdClass)).Cast<PropertyDescriptor>().Where(x => !x.IsReadOnly && x.PropertyType == typeof(string)))
overrides.Add(typeof(XsdClass), prop.Name, attribs);
XmlSerializer serializer = new XmlSerializer(typeof(XsdClass), overrides);
string result;
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, xc);
result = textWriter.ToString();
}
}
public class XsdClass
{
public string SomeString1 { get; set; }
public string SomeString2 { get; set; }
public string SomeString3 { get; set; }
}

How to serialize a custom object with a list of another custom object in a particular schema when this list of custom objects is a generic type

I need to create a lot of XML files. They are very similar and they need to have the following schema:
<Servidor>
<VersaoLayout>0001</VersaoLayout> <!--Will not change-->
<CodigoUJ>001001</CodigoUJ> <!--Will not change-->
<ItemServidor> <!--A list of <T> that will change-->
<CPFServidor>13579024681</CPFServidor>
<NomeServidor>Fulano Pereira Tal</NomeServidor>
</ItemServidor>
</Servidor>
Note that the tags <VersaoLayout> and <CodigoUJ> are the same for all the files i need to create, the <ItemServidor> is the content that will vary. So i thought: Why not create just one class and use generics to handle the differences for this case? And for now my classes are the following:
//Kind of a container class that will hold the content that will vary.
[XmlRoot("Servidor", ElementName="Servidor")]
public class LayoutArquivoXML<T> where T : ItemLayout
{
[XmlElement("VersaoLayout")]
public string VersaoLayout { get; set; }
[XmlElement("CodigoUJ")]
public string CodigoUJ { get; set; }
//[XmlArrayItem("ItemServidor")]
public List<T> ItensLayout { get; set; }
// Constructors omited for simplicity
}
//A "ContentClass". I will have a bunch of classes similar to this
[XmlRoot("ItemServidor", ElementName = "ItemServidor")]
public class ServidorLayout : ItemLayout
{
[XmlElement("CPFServidor")]
public string CPFServidor { get; set; }
[XmlElement("NomeServidor")]
public string Nome { get; set; }
}
I am instantiating my "container class" this way:
LayoutArquivoXML<ServidorLayout> layout = new Layout.LayoutArquivoXML<ServidorLayout>("versao01", "999", lstItensLayout.ToList());
And my Serialization method is the following:
public string Serializador<T>(T objeto)
{
string xml = string.Empty;
using (var sw = new ISO8859StringWriter())
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); // to omit XML namespaces
var xs = new XmlSerializer(typeof(T));
xs.Serialize(sw, objeto, ns);
}
return xml;
}
These objects graph are generating a XML with the following schema:
<Servidor>
<VersaoLayout>versao01</VersaoLayout>
<CodigoUJ>999</CodigoUJ>
<ItensLayout>
<ServidorLayout>
<CPFServidor>4252813450</CPFServidor>
<NomeServidor>Antonio de Sousa Muniz</NomeServidor>
</ServidorLayout>
</ItensLayout>
</Serv>
I dont want the tag <ItensLayout> tag in the XML. What i need to do to generate the XML in the desired schema?
Also, the root tag <Servidor> will need to change according to the file i am generating, imagine that i need to create another file where the root tag is Another, let say:
LayoutArquivoXML<AnotherLayout> layout = new Layout.LayoutArquivoXML<AnotherLayout>("versao01", "999", anotherItensLayout.ToList());
There are two separate issues.
I don't want the tag <ItensLayout> tag in the XML. What i need to do to generate the XML in the desired schema?
As explained in the documentation for Serializing an Array as a Sequence of Elements, to serialize an array (list, enumerable etc.) as flat sequence of XML elements, you need to apply XmlElement attribute to the property/field.
Also, the root tag <Servidor> will need to change according to the file i am generating
This is controlled by the XmlRoot attribute.
Now, all this applies if you are using concrete classes. In order to apply them dynamically to your generic class, you can use the XmlOverrides class and the XmlSerializer constructors accepting such parameter.
To do that, first add an optional parameter to your original method:
public string Serializador<T>(T objeto, XmlAttributeOverrides overrides = null)
{
string xml = string.Empty;
using (var sw = new ISO8859StringWriter())
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); // to omit XML namespaces
var xs = new XmlSerializer(typeof(T), overrides);
xs.Serialize(sw, objeto, ns);
xml = sw.ToString();
}
return xml;
}
Then create a specific method like this:
public string Serializador<T>(LayoutArquivoXML<T> objeto, string rootElementName, string contentElementName)
where T : ItemLayout
{
var xmlAttrOverrides = new XmlAttributeOverrides();
// Root element name override
var xmlRootAttrs = new XmlAttributes();
xmlRootAttrs.XmlRoot = new XmlRootAttribute(rootElementName);
xmlAttrOverrides.Add(typeof(LayoutArquivoXML<T>), xmlRootAttrs);
// Content element name override
var xmlContentAttrs = new XmlAttributes();
xmlContentAttrs.XmlElements.Add(new XmlElementAttribute(contentElementName));
xmlAttrOverrides.Add(typeof(LayoutArquivoXML<T>), "ItensLayout", xmlContentAttrs);
// Call the original method passing the overrides
return Serializador(objeto, xmlAttrOverrides);
}
Now you can achieve your goal by using something like this:
var source = new LayoutArquivoXML<ServidorLayout>
{
VersaoLayout = "0001",
CodigoUJ = "001001",
ItensLayout = new List<ServidorLayout>
{
new ServidorLayout
{
CPFServidor = "13579024681",
Nome = "Fulano Pereira Tal",
},
}
};
var xml = Serializador(source, "Servidor", "ItemServidor");
which produces:
<Servidor>
<VersaoLayout>0001</VersaoLayout>
<CodigoUJ>001001</CodigoUJ>
<ItemServidor>
<CPFServidor>13579024681</CPFServidor>
<NomeServidor>Fulano Pereira Tal</NomeServidor>
</ItemServidor>
</Servidor>

Serialize/Deserialize different property names?

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

C# Xml serialization, collection and root element

My app serializes objects in streams.
Here is a sample of what I need :
<links>
<link href="/users" rel="users" />
<link href="/features" rel="features" />
</links>
In this case, the object is a collection of 'links' object.
-----------First version
At first I used the DataContractSerializer, however you cannot serialize members as attributes (source)
Here is the object :
[DataContract(Name="link")]
public class LinkV1
{
[DataMember(Name="href")]
public string Url { get; set; }
[DataMember(Name="rel")]
public string Relationship { get; set; }
}
And here is the result :
<ArrayOflink xmlns:i="...." xmlns="...">
<link>
<href>/users</href>
<rel>users</rel>
</link>
<link>
<href>/features</href>
<rel>features</rel>
</link>
</ArrayOflink>
----------- Second version
Ok, not quiet what I want, so I tried the classic XmlSerializer, but... oh nooo, you cannot specify the name of the root element & of the collection's elements if the root element is a collection...
Here is the code :
[XmlRoot("link")]
public class LinkV2
{
[XmlAttribute("href")]
public string Url { get; set; }
[XmlAttribute("rel")]
public string Relationship { get; set; }
}
Here is the result :
<ArrayOfLinkV2>
<LinkV2 href="/users" rel="users" />
<LinkV2 href="/features" rel="features" />
<LinkV2 href="/features/user/{keyUser}" rel="featuresByUser" />
</ArrayOfLinkV2>
----------- Third version
using XmlSerializer + a root element :
[XmlRoot("trick")]
public class TotallyUselessClass
{
[XmlArray("links"), XmlArrayItem("link")]
public List<LinkV2> Links { get; set; }
}
And its result :
<trick>
<links>
<link href="/users" rel="users" />
<link href="/features" rel="features" />
<link href="/features/user/{keyUser}" rel="featuresByUser" />
</links>
</trick>
Nice, but I don't want that root node !!
I want my collection to be the root node.
Here are the contraints :
the serialization code is generic, it works with anything serializable
the inverse operation (deserialization) have to work too
I don't want to regex the result (I serialize directly in an output stream)
What are my solutions now :
Coding my own XmlSerializer
Trick XmlSerializer when it works with a collection (I tried, having it find a XmlRootElement and plurialize it to generate its own XmlRootAttribute, but that causes problem when deserializing + the items name still keeps the class name)
Any idea ?
What really bother me in that issue, is that what I want seems to be really really really simple...
Ok, here is my final solution (hope it helps someone), that can serialize a plain array, List<>, HashSet<>, ...
To achieve this, we'll need to tell the serializer what root node to use, and it's kind of tricky...
1) Use 'XmlType' on the serializable object
[XmlType("link")]
public class LinkFinalVersion
{
[XmlAttribute("href")]
public string Url { get; set; }
[XmlAttribute("rel")]
public string Relationship { get; set; }
}
2) Code a 'smart-root-detector-for-collection' method, that will return a XmlRootAttribute
private XmlRootAttribute XmlRootForCollection(Type type)
{
XmlRootAttribute result = null;
Type typeInner = null;
if(type.IsGenericType)
{
var typeGeneric = type.GetGenericArguments()[0];
var typeCollection = typeof (ICollection<>).MakeGenericType(typeGeneric);
if(typeCollection.IsAssignableFrom(type))
typeInner = typeGeneric;
}
else if(typeof (ICollection).IsAssignableFrom(type)
&& type.HasElementType)
{
typeInner = type.GetElementType();
}
// yeepeeh ! if we are working with a collection
if(typeInner != null)
{
var attributes = typeInner.GetCustomAttributes(typeof (XmlTypeAttribute), true);
if((attributes != null)
&& (attributes.Length > 0))
{
var typeName = (attributes[0] as XmlTypeAttribute).TypeName + 's';
result = new XmlRootAttribute(typeName);
}
}
return result;
}
3) Push that XmlRootAttribute into the serializer
// hack : get the XmlRootAttribute if the item is a collection
var root = XmlRootForCollection(type);
// create the serializer
var serializer = new XmlSerializer(type, root);
I told you it was tricky ;)
To improve this, you can :
A) Create a XmlTypeInCollectionAttribute to specify a custom root name (If the basic pluralization does not fit your need)
[XmlType("link")]
[XmlTypeInCollection("links")]
public class LinkFinalVersion
{
}
B) If possible, cache your XmlSerializer (in a static Dictionary for example).
In my testing, instanciating a XmlSerializer without the XmlRootAttributes takes 3ms.
If you specify an XmlRootAttribute, it takes around 80ms (Just to have a custom root node name !)
XmlSerializer should be able to do what you need, but it is highly dependent on the initial structure and setup. I use it in my own code to generate remarkably similar things.
public class Links<Link> : BaseArrayClass<Link> //use whatever base collection extension you actually need here
{
//...stuff...//
}
public class Link
{
[XmlAttribute("href")]
public string Url { get; set; }
[XmlAttribute("rel")]
public string Relationship { get; set; }
}
now, serializing the Links class should generate exactly what you are looking for.
The problem with XmlSerializer is when you give it generics, it responds with generics. List implemets Array somewhere in there and the serialized result will nearly always be ArrayOf<X>. To get around that you can name the property, or the class root. The closes to what you need is probably the Second Version from your examples. Im assuming you attempted direct serialization of an object List Links. That wouldn't work because you didn't specify the root node. Now, a similar approach can be found here. In this one they specify the XmlRootAttribute when declaring the serializer. yours would look like this:
XmlSerializer xs = new XmlSerializer(typeof(List<Link>), new XmlRootAttribute("Links"));
Here you go...
class Program
{
static void Main(string[] args)
{
Links ls = new Links();
ls.Link.Add(new Link() { Name = "Mike", Url = "www.xml.com" });
ls.Link.Add(new Link() { Name = "Jim", Url = "www.xml.com" });
ls.Link.Add(new Link() { Name = "Peter", Url = "www.xml.com" });
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Links));
StringWriter stringWriter = new StringWriter();
xmlSerializer.Serialize(stringWriter, ls);
string serializedXML = stringWriter.ToString();
Console.WriteLine(serializedXML);
Console.ReadLine();
}
}
[XmlRoot("Links")]
public class Links
{
public Links()
{
Link = new List<Link>();
}
[XmlElement]
public List<Link> Link { get; set; }
}
[XmlType("Link")]
public class Link
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlAttribute("Href")]
public string Url { get; set; }
}

Categories