Search code examples
c#xmldatasetstrongly-typed-dataset

Is DataSet supports complex type when GetXml method?


I have the schema below:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="WSParam">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="domain" type="xs:string"/>
                <xs:element name="userName" type="xs:string"/>
                <xs:element name="Cases">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="caseName" type="xs:string"/>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

I expected the output of DataSet.GetXml() below:

<NewDataSet>
    <WSParam>
        <domain>ad</domain>
        <userName>admin</userName>
        <Cases>
            <caseName>case 1</caseName>
        </Cases>
    </WSParam>
</NewDataSet>

But the output is:

<NewDataSet>
    <WSParam>
        <domain>ad</domain>
        <userName>admin</userName>
    </WSParam>
    <Cases>
        <caseName>case 1</caseName>
    </Cases>
</NewDataSet>

Anyone confirm me the correct behavior of DataSet.GetXml() with complex type or I miss something.

Any guidance is highly appreciated.

UPDATES1:

I am using DataSet.ReadXmlSchema("...xsd"). From that, I use DataSet.Tables[..] to create DataTable to put the data. Then call DataSet.GetXml() to get the string XML representation.

UPDATES2:

Code:

private void button2_Click(object sender, EventArgs e)
{
    String sXSD = @"C:\WindowsFormsApplication1\WindowsFormsApplication1\XMLFile1.xsd";
    DataSet dsXSD = new DataSet();
    dsXSD.ReadXmlSchema(sXSD);
    int k = 0;
    do
    {
        DataRow d;
        d = dsXSD.Tables["tblUser"].NewRow();
        int i = 0;
        do
        {
            d[i] = k;
            i = (i + 1);
        } while (i < dsXSD.Tables["tblUser"].Columns.Count);
        dsXSD.Tables["tblUser"].Rows.Add(d);
        d = dsXSD.Tables["tblUserRole"].NewRow();
        i = 0;
        do
        {
            d[i] = k;
            i = (i + 1);
        } while (i < dsXSD.Tables["tblUserRole"].Columns.Count);
        dsXSD.Tables["tblUserRole"].Rows.Add(d);
        k = (k + 1);
    } while (k < 3);
    dsXSD.WriteXml("c:\\test.xml");


}

XSD:

<?xml version="1.0" encoding="utf-16" ?>
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified" version="1.0"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xsd:element name="tblUser">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="UserID" type="xsd:int" />
        <xsd:element name="FullName" type="xsd:string" nillable="true" />
        <xsd:element name="tblUserRole">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="UserID" type="xsd:int" />
              <xsd:element name="RoleID" type="xsd:int" />
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

OUTPUT:

<?xml version="1.0" standalone="yes"?>
<NewDataSet>
  <tblUser>
    <UserID>0</UserID>
    <FullName>0</FullName>
    <tblUserRole>
      <UserID>0</UserID>
      <RoleID>0</RoleID>
    </tblUserRole>
  </tblUser>
  <tblUser>
    <UserID>1</UserID>
    <FullName>1</FullName>
    <tblUserRole>
      <UserID>1</UserID>
      <RoleID>1</RoleID>
    </tblUserRole>
  </tblUser>
  <tblUser>
    <UserID>2</UserID>
    <FullName>2</FullName>
    <tblUserRole>
      <UserID>2</UserID>
      <RoleID>2</RoleID>
    </tblUserRole>
  </tblUser>
</NewDataSet>

Solution

  • I'm not sure if it is possible using datasets.

    But you could do it yourself by creating a non standard microsoft dataset xsd. It requires a little work but you can afterwards tweak it as you like.

    The key is to use an xs:group.

    <?xml version="1.0" encoding="utf-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
      <xs:element name="NewDataSet">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="WSParam">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="domain" type="xs:string" minOccurs="0" />
                  <xs:element name="userName" type="xs:string" minOccurs="0" />
                  <xs:group ref="CasesGroup" minOccurs="0"/>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    
        <xs:group name="CasesGroup">
        <xs:sequence>
          <xs:element name="Cases">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="CaseName" minOccurs="1" maxOccurs="unbounded">
                  <xs:complexType>
                    <xs:simpleContent>
                      <xs:extension base="xs:string">
                      </xs:extension>
                    </xs:simpleContent>
                  </xs:complexType>
                </xs:element>
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:sequence>
      </xs:group>
    </xs:schema>
    

    For the above xsd (which I named wsparam.xsd) run "xsd.exe wsparam.xsd /classes /n:Yournamespace"

    Now you can fill the data from code

        NewDataSetWSParam p = new NewDataSetWSParam();
        p.domain = "ad";
        p.userName = "admin";
        p.Cases = new NewDataSetWSParamCaseName[] {  
                new NewDataSetWSParamCaseName () { Value ="case 1" }
        };
    

    Now comes the tricky part Using a non standard XmlTextWriter when serializing to strip namespace attributes from the XML file

    public class NoNamespaceXmlWriter : XmlTextWriter
    {
                public NoNamespaceXmlWriter(System.IO.TextWriter output)
                    : base(output)
                {
                    Formatting = System.Xml.Formatting.None;
                }
    
                public override void WriteStartDocument() { }
                public override void WriteStartElement(string prefix, string localName, string ns)
                {
                    base.WriteStartElement("", localName, "");
                }
            }
    

    Use the code below to Serialize you object

    string serialXML = string.Empty;
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "");
    
            XmlSerializer serializer = new XmlSerializer(p.GetType());
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            using (XmlWriter writer = new RDECommon.Xml.NoNamespaceXmlWriter(sw))
            {
                serializer.Serialize(writer, p, ns);
                serialXML = sb.ToString();
            }
    

    serialXML contains your xml

    <NewDataSet>
        <WSParam>
            <domain>ad</domain>
            <userName>admin</userName>
            <Cases>
                <caseName>case 1</caseName>
            </Cases>
        </WSParam>
    </NewDataSet>