1

I have a .dtd file which has been converted to a .xsd file. This file has an element Identity:

  <xs:element name="Identity">
    <xs:complexType mixed="true">
      <xs:sequence>
        <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##any" />
      </xs:sequence>
      <xs:attribute name="lastChangedTimestamp" type="xs:string" />
    </xs:complexType>
  </xs:element>

This generates the following sample xml, with some text content:

<Identity lastChangedTimestamp="lastChangedTimestamp1" xmlns="http://tempuri.org/cXML">text</Identity>

I converted the .xsd to a .cs file using xsd.exe:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://tempuri.org/cXML")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://tempuri.org/cXML", IsNullable = false)]
public partial class Identity
{
    public string Text { get; set; }
    private System.Xml.XmlNode[] anyField;

    private string lastChangedTimestampField;

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

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string lastChangedTimestamp
    {
        get
        {
            return this.lastChangedTimestampField;
        }
        set
        {
            this.lastChangedTimestampField = value;
        }
    }
}

However this does not allow me to set the text content of the identity node:

var credential = new Credential()
{
    Identity = new Identity()
    {
        lastChangedTimestamp = "lastChangedTimestamp1",
        //Text = "MyRef" <- would like to set it at this point
    }
};

Is there anything I can do to ensure that when I serialize the Identity object I can get my ref into the text content of the node, or is this not supported?

<Identity lastChangedTimestamp="lastChangedTimestamp1">
    MyRef
</Identity>
dbc
  • 104,963
  • 20
  • 228
  • 340
Aleph
  • 199
  • 2
  • 13
  • May I assume that you added `public string Text { get; set; }` manually? – dbc Jul 26 '19 at 10:26
  • The "Text" is the property I'd assume I would be able to set, but doesn't exist in the auto generated code. I'd like to avoid changing the code generated by the system – Aleph Jul 26 '19 at 10:30

2 Answers2

1

mixed="true" in your XSD indicates that the <Identity> element can contain mixed content:

An element type has mixed content when elements of that type may contain character data, optionally interspersed with child elements.

Furthermore, the child elements are unconstrained by the XSD.

xsd.exe and XmlSerializer support mixed content via a combination of the [XmlText] and [XmlAnyElement] attributes. When both attributes are applied to a member of type XmlNode[], then all mixed content contained in the XML will be bound to that member.

In this case, you will note that xsd.exe has created a public System.Xml.XmlNode[] Any member with these attributes, so, to add the text "MyRef" to your XML, you need to add an appropriate XmlText to this array:

var identity = new Identity()
{
    lastChangedTimestamp = "lastChangedTimestamp1",
    Any = new XmlNode []
    {
        new XmlDocument().CreateTextNode("MyRef"),
    },
};

Demo fiddle here.

Alternatively, you could manually change the Any property to be an array of objects as shown in this answer to XmlSerializer - node containing text + xml + text by Sruly :

[XmlText(typeof(string))]
[XmlAnyElement]
public object[] Any { get; set; }

Then you would be able to add the string directly to the array:

var identity = new Identity()
{
    lastChangedTimestamp = "lastChangedTimestamp1",
    Any = new object []
    {
        "MyRef",
    },
};

(You would still need to allocate XmlNode objects for more complex XML.)

Demo fiddle #2 here.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • That works great, thanks. I haven't worked much with XML so it's quite counter-intuitive to create a XML Document to store a string but that's the way to do it – Aleph Jul 26 '19 at 11:03
0

In the end I updated the xsd Identity node to add <xs:extension base="xs:string">:

  <xs:element name="Identity">
    <xs:complexType mixed="true">
      <xs:simpleContent>
        <xs:extension base="xs:string">
          <xs:attribute name="lastChangedTimestamp" type="xs:string" />
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>

This created a Text array in my object

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://tempuri.org/cXML")]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://tempuri.org/cXML", IsNullable = false)]
    public partial class Identity
    {

        private string lastChangedTimestampField;

        private string[] textField;

        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public string lastChangedTimestamp
        {
            get
            {
                return this.lastChangedTimestampField;
            }
            set
            {
                this.lastChangedTimestampField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlTextAttribute()]
        public string[] Text
        {
            get
            {
                return this.textField;
            }
            set
            {
                this.textField = value;
            }
        }
    }

Which I can then use to set my ref:

new Credential()
{
    Identity = new Identity()
    {
        Text=new string[]
        {
            "MyRef"
        }
    }
}

Not sure if this is the best way but I can use this for now.

Aleph
  • 199
  • 2
  • 13