简体   繁体   中英

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> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM