How to use Complex types with xs:any / ##any and mixed in code generated by the XSD tool
Asked Answered
C

2

7

I have the following complex type in my XML schema:

<xs:complexType name="Widget" mixed="true">
    <xs:sequence>
        <xs:any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
</xs:complexType>

The element in derived XML could contain string or could contained wellformed XML, hence the mixed attribute being true.

When I run this through the .NET XSD Tool I get the following generate code:

public partial class Widget{

    private System.Xml.XmlNode[] anyField;

    /// <remarks/>
    [System.Xml.Serialization.XmlTextAttribute()]
    [System.Xml.Serialization.XmlAnyElementAttribute()]
    public System.Xml.XmlNode[] Any {
        get {
            return this.anyField;
        }
        set {
            this.anyField = value;
        }
    }
}

The question I have is that I am not entirely sure how I should then use this. Ultimately I need to be able to set the value of widget to either:

<widget>Hello World!</widget>

or

<widget>
  <foo>Hello World</foo>
</widget>

Both of which validate agaisnt the schema

Controversial answered 11/2, 2011 at 13:14 Comment(0)
R
2

For this:

<widget>  
    <foo>Hello World</foo>
</widget>

Use this:

XmlDocument dom = new XmlDocument();
Widget xmlWidget = new Widget();
xmlWidget.Any = new XmlNode[1];
xmlWidget.Any[0] = dom.CreateNode(XmlNodeType.Element, "foo", dom.NamespaceURI);
xmlWidget.Any[0].InnerText = "Hello World!";

For this:

<widget>Hello World!</widget>

Use this:

XmlDocument dom = new XmlDocument();
XmlNode node = dom.CreateNode(XmlNodeType.Element, "foo", dom.NamespaceURI);
node.InnerText = "Hello World";

Widget w = new Widget();
w.Any = new XmlNode[1];
w.Any[0] = node.FirstChild; 
Ricercar answered 11/2, 2011 at 21:22 Comment(4)
That won't work, you haven't initialised xmlWidget.Any[0]. So it would throw a null reference exceptionControversial
I updated my answer to deal with the NullReferenceExceptions. However, I'm not sure that you'll be able to accomplish the "inner-text-only" option. to do this in the code would mean that the Widget class could both function as a string type, as well as an array of Nodes. I don't believe this is possible with standard XSD generation.Ricercar
I made an edit which is pretty similiar to the code you posted. I'm not sure that you're going to find a different way to do this. Really, you're trying to override the Widget property. In one instance you want it to be just a string and in other instances you want an array of nodes, but you can't define a property of the same name multiple times in a single class.Ricercar
To be more explicit (and show your code's intentions), you can replace the call node.FirstChild with dom.CreateTextNode("Hello World").Supernatural
C
0

Posting this as a answer as technically it works and answers the question. However it seems like a really nasty hack. So if anybody has an alternative and better solution I am all ears.

string mystring= "if I check this code in it will at least have comedy value";

XmlDocument thisLooksBad = new XmlDocument();
thisLooksBad.LoadXml("<temp>" + mystring + "</temp>");

Widget stringWidget = new Widget();
stringWidget.Any = new XmlNode[1];
stringWidget.Any[0] = thisLooksBad.SelectSingleNode("/temp").FirstChild;

As you can see I am placing my string into an XmlDocument wrapped in tags, this works, compiles and serializes without issues - so yes it is a solution but it strikes me that this is a nasty hack.

string myxml = "<x><y>something</y></x>";

XmlDocument thisDoesntLookSoBad = new XmlDocument();
thisLooksBad.LoadXml(myxml);

Widget xmlWidget = new Widget();
xmlWidget.Any = new XmlNode[1];
xmlWidget.Any[0] = thisDoesntLookSoBad;

In this example I am placing my XML into an XmlDocument and then assigning that to the generated class. This makes more sense as I am working with XML not raw string.

Curiously I can also do this and it works as well (but is also a nasty hack):

string myxml = "<x><y>something</y></x>";

XmlDocument dom = new XmlDocument();
dom.LoadXml("<temp>" + myxml + "</temp>");

Widget xmlWidget = new Widget();
xmlWidget.Any = new XmlNode[1];
xmlWidget.Any[0] = dom.SelectSingleNode("/temp").FirstChild;
Controversial answered 11/2, 2011 at 21:10 Comment(3)
Which one of these examples satifies the <widget>Hello World!</widget> requirement.Ricercar
The first sample deserialises to : <widget>if I check this code in it will at least have comedy value</widget>Controversial
Like you say, this gets the job done but I perfer pmartin's solution because it is more robust i.e. not using strings concatention to build the Xml DOM.Supernatural

© 2022 - 2024 — McMap. All rights reserved.