19

If I have a class marked as a DataContract and a few properties on it marked with DataMember attributes I can serialize it out to XML easily, but it would create output like:

<Person>
    <Name>John Smith</Name>
    <Email>john.smith@acme.com</Email>
    <Phone>123-123-1234</Phone>
</Person>

What I would prefer is attributes, like...

<Person Name="John Smith" Email="john.smith@acme.com" Phone="123-123-1234" />

The DataMember attribute allows me to control the Name and Order but not whether it is serialized as an element or attribute. I have looked around and found DataContractFormat and IXmlSerializable but I am hoping there is there an easier solution.

What is the easiest way to do this?

Daniel Schilling
  • 4,829
  • 28
  • 60
Brennan
  • 11,546
  • 16
  • 64
  • 86

3 Answers3

40

You can do this with the DataContractSerializer - the answer is to take over the Xml serialization yourself by implementing the IXmlSerializable interface. For write-only support - you can leave the implementation of ReadXml empty, and return null for GetSchema, and then write the implementation of WriteXml as follows:

public class MyPerson : IXmlSerializable
{
  public string Name { get; set;}
  public string Email { get; set;}
  public string Phone { get; set;}

  public XmlSchema GetSchema() { return null; }
  public void ReadXml(XmlReader reader) { }
  public void WriteXml(XmlWriter writer)
  { 
    writer.WriteAttributeString("name", Name);
    writer.WriteAttributeString("email", Email);
    writer.WriteAttributeString("phone", Phone);
  } 
}

If you're using the same type for, say, JSON serialization as well, then you are still free to add the DataContract and DataMember attributes - the DataContractSerializer will utilise the IXmlSerializable interface implementation only when writing Xml.

I blogged about this here.

Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
  • Are you certain that the `DataContractSerializer` will invoke the `WriteXml` method of this code? – John Saunders Aug 17 '09 at 15:33
  • 1
    I know - it seems unlikely - but I've just posted an article on our corporate blog at http://www.labs.jobserve.com/Articles.aspx/Building-Labs--Writing-an-OpenSearch-Suggestions-provider-in-C-with-WCF where you can download source code that shows it in action (using it to implement OpenSearch suggestions). The clue came from this MSDN content: http://msdn.microsoft.com/en-us/library/ms731923.aspx. – Andras Zoltan Aug 17 '09 at 16:28
  • 1
    Yes, thanks for that, it seems that the correct implementation should use the XmlSchemaProviderAttribute to be considered properly implemented. It's incredible the amount of gotchas there are with this - but I have to say that the most basic solution has worked wonders for our code. At least this means that you don't necessarily have to rely upon XmlSerializer! – Andras Zoltan Aug 17 '09 at 22:20
  • @AndrasZoltan DataContractJsonSerializer *will* use the IXmlSerializable interface implementation – Cœur Apr 11 '14 at 11:46
  • Cool - in the modern era we're more likely to be using JSON.Net, though. – Andras Zoltan Apr 12 '14 at 08:58
  • 1
    This also works fine for reading, I just had to set the `XmlRoot` attribute. Example: to parse `` I used `[XmlRoot(ElementName = "Fault", Namespace = "http://com.sap/aii/proxy/xiruntime/")] public class Fault : IXmlSerializable` – user247702 Apr 26 '16 at 11:39
17

You can't do this with the DataContractSerializer; if you want attributes you need to use the XmlSerializer instead. With the DataContractSerializer class a more restrictive subset of the XML specification is permitted which improves performance, and improves the interoperability of published services, but gives you rather less control over the XML format.

If you're using WCF services then take a look at XmlSerializerFormatAttribute which allows you to use the XmlSerializer for serialization.

Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • 1
    There has to be a simple way to make it use attributes. I do not care about web services, I just to output the XML in a specific format. – Brennan Feb 26 '09 at 19:31
  • Hi, I', afraid Greg is correct - see http://msdn.microsoft.com/en-us/library/ms731923.aspx for the limitations of the DataContractSerializer. – larsw Feb 26 '09 at 19:36
  • 1
    If you don't care about web services, then why are you using WCF? Are you just trying to serialize some classes? If so, then use the XmlSerializer by itself. – John Saunders Feb 27 '09 at 05:29
  • 7
    So strange how WCF claims all this enterop capabilities and it can't do this. So many schemas for services online require attributes in xml. I understand performance, but let me choose so i can talk to other services. – capdragon Jan 25 '12 at 14:44
0

You could convert back and forth between attributes and elements when serializing/deserializing. The following works for the latter.

    private XmlReader AttributesToElements( Stream stream )
    {
            var root = XElement.Load( stream );
            foreach ( var element in root.Descendants() ) {
                    foreach ( var attribute in element.Attributes() )
                            element.Add( new XElement( root.Name.Namespace + attribute.Name.LocalName, (string)attribute ) );
                    element.Attributes().Remove();
            }
            return root.CreateReader();
    }
HappyNomad
  • 4,458
  • 4
  • 36
  • 55