views:

384

answers:

3

So I am trying to simply decorate a class to serialize it as XML. Here's an example of my problem.

[XmlElement("Dest")]
    public XmlNode NewValue { get; set; }

The real problem here is that sometimes in this implementation the XmlNode can be an XmlElement or XmlAttribute. when it's an element this code works fine, but when it comes through as an attribute the serializer throws the following error:

System.InvalidOperationException: Cannot write a node of type XmlAttribute as an element value. Use XmlAnyAttributeAttribute with an array of XmlNode or XmlAttribute to write the node as an attribute.

I tried the XmlAnyAttribute, but that failed as well. So simply put, how can I serialize an XmlNode?

For the record, I marked the correct answer below. You kinda got to hack it. Here's roughly what I implemented myself in case anyone else hits this.

    [XmlIgnore()]
    public XmlNode OldValue { get; set; }

    [XmlElement("Dest")]
    public XmlNode SerializedValue
    {
        get
        {
            if (OldValue == null)
            {
                return null;
            }
            if (OldValue.NodeType == XmlNodeType.Attribute)
            {
                XmlDocumentFragment frag = OldValue.OwnerDocument.CreateDocumentFragment();

                XmlElement elem = (frag.OwnerDocument.CreateNode(XmlNodeType.Element, "SerializedAttribute", frag.NamespaceURI) as XmlElement);

                elem.SetAttribute(OldValue.Name, OldValue.Value);

                return elem;
            }
            else
            {
                return OldValue;
            }
        }
        set
        {
            if (value == null)
            {
                OldValue = null;
                return;
            }
            if ((value.Attributes != null) && (value.NodeType == XmlNodeType.Element) && ((value.ChildNodes == null) || (value.ChildNodes.Count == 0)))
            {
                OldValue = value.Attributes[0];
            }
            else
            {
                OldValue = value;
            }
        }
    }
A: 

It seems odd that you are mixing Xml Serialization with XmlNodes, and like you said that node could be an attribute or an element. Normally you would serialize an XmlNode as part of an XmlDocument by using XmlDocument.Save() functions.

I think it might be easier to serialize the XmlNode as a string. Something like this maybe:

[XmlIgnore]
public XmlNode NewValue { get; set; }

[XmlElement("Dest")]
public string NewValueString { get; set; }
{ 
    get { return NewValue.OuterXml; } // Edit: this property can't directly
    set { NewValue.OuterXml = value; } // set OuterXml
}

Massive Edit:

On second thought ....

You are mixing two different ways of dealing with Xml and there is no direct way to get this to work.

You can't use Xml Serialization with a property of type XmlNode, or XmlAttribute, or XmlElement. XmlNode and its subclasses don't have a public parameterless constructor required for Xml Serialization. They can only be created through a XmlDocument. Also, Xml Serialization works by reflection and saving all public get/set properties and XmlNode and subclasses don't have properties designed for this.

You can still serialize the XmlNode as a string (you just can't implement it directly like I show above) then you will have to do some post-processing step after xml deserialization to recreate the XmlNode.

But again, you are mixing two different ways of dealing with Xml and that raises some warning flags in my mind.

Brian Ensink
Thanks for the answer but,A. The OuterXml property is ReadOnly. You can't set it's value.B. Even if you could, the XmlAttribute subclass does not support this property.
highphilosopher
Ah nuts I forgot about that.
Brian Ensink
+1  A: 

You can try to hack it (I agree its not very nice):

XmlNode v;

[XmlElement("Dest")]
public XmlNode NewValue { get{return v as XmlElement;} set{v = value;} }

[XmlAttribute("Dest")]
public NewValue { get{return v as XmlAttribute;} set{v = value;} }
Grzenio
A: 

Since you cannot mix and match with the default mechanisms, you would more than likely need to write your own serializer. By doing that you could switch your code to export / import the data however you want with logic statements around your object.

Joshua Cauble