Zayum
Zayum

Reputation: 119

C#/.net Preserve Schema and XML when calling XMLWriter() function from dataset

I have an xml file

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfLocations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Location xsi:type="JointLocation">
    <Name>Example Location</Name>
    <Index>0</Index>
    <ZClearance>0</ZClearance>
    <Joint1>100</Joint1>
    <Joint2>200</Joint2>
    <Joint3>200</Joint3>
    <Joint4>200</Joint4>
    <Joint5>200</Joint5>
    <joint6>0</joint6>
    <Joint6>0</Joint6>
  </Location>
</ArrayOfLocations>

I load this file into a data set, and then into a DataGridView. From that DataGridView I can add new Location elements, or edit existing Location elements and save. When I save, I am doing this

string path = filePathBox.Text;
DataSet ds = (DataSet)dataGridView1.DataSource;
ds.WriteXml(filePathBox.Text);

After saving, the XML file then looks like

<?xml version="1.0" standalone="yes"?>
<ArrayOfLocations>
  <Location>
    <Name>Example Location</Name>
    <Index>0</Index>
    <ZClearance>0</ZClearance>
    <Joint1>100</Joint1>
    <Joint2>200</Joint2>
    <Joint3>200</Joint3>
    <Joint4>200</Joint4>
    <Joint5>200</Joint5>
    <joint6>0</joint6>
    <Joint6>0</Joint6>
  </Location>
</ArrayOfLocations>

As you can see the xsi and namespace have been removed. I would like to preserve these attributes.

So far I have tried adding as an additional parameter to WriteXML():

ds.WriteXML(filepath, XmlWriteMode.WriteSchema)

However, this creates a big mess and still does not maintain the initial format that I want to preserve. Any tips?

Upvotes: 0

Views: 184

Answers (1)

Slack Groverglow
Slack Groverglow

Reputation: 856

A simple example shows us that ReadXML/WriteXML will lose the schema info you're interested in

using (FileStream fs = new FileStream("D:\\Workspace\\FormTest\\input.xml", FileMode.Open))
{
    DataSet ds = new DataSet();
    ds.ReadXml(fs);
    ds.WriteXml(Console.Out);
}

Gives us

<ArrayOfLocations>
  <Location>
    <Name>Example Location</Name>
    <Index>0</Index>
    <ZClearance>0</ZClearance>
    <Joint1>100</Joint1>
    <Joint2>200</Joint2>
    <Joint3>200</Joint3>
    <Joint4>200</Joint4>
    <Joint5>200</Joint5>
    <joint6>0</joint6>
    <Joint6>0</Joint6>
  </Location>
</ArrayOfLocations>

The best way I found to recover this schema information is to Deserialize the data. It's not pretty but it worked for me:

XmlSerializer s = new XmlSerializer(typeof(ArrayOfLocations));
ArrayOfLocations fix = new ArrayOfLocations();
using (FileStream fs = new FileStream("D:\\Workspace\\FormTest\\input.xml", FileMode.Open))
{
    DataSet ds = new DataSet();
    ds.ReadXml(fs);
    string xml = ds.GetXml();
    ArrayOfLocations input = (ArrayOfLocations)s.Deserialize(new StringReader(xml));
    
    foreach(var location in input)
    {
        fix.Add(new JointLocation()
        {
            Name = location.Name,
            ...
            Joint6 = location.Joint6 
        });
    }
}

XmlTextWriter xtw = new XmlTextWriter(Console.Out);
xtw.Formatting = Formatting.Indented;
s.Serialize(xtw, fix);

And of course you'll need to create the classes that model your schema in order to deserialize:

public class Location
{
    public string Name { get; set; }
    ...
    public byte Joint6 { get; set; }
}

public class JointLocation : Location { }

[XmlInclude(typeof(JointLocation))]
[XmlRoot("ArrayOfLocations")]
public class ArrayOfLocations : List<Location> { }

Note the following gotchas

  1. You need to define a JoinLocation class, specify that type under XmlInclude, and manually fix your object all in order to comply with the schema that you want to output to.
  2. You will need to use a XmlTextWritter to respect indentation

This all should give you the desired output:

<?xml version="1.0" encoding="Codepage - 437"?>
<ArrayOfLocations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Location xsi:type="JointLocation">
    <Name>Example Location</Name>
    <Index>0</Index>
    <ZClearance>0</ZClearance>
    <Joint1>100</Joint1>
    <Joint2>200</Joint2>
    <Joint3>200</Joint3>
    <Joint4>200</Joint4>
    <Joint5>200</Joint5>
    <Joint6>0</Joint6>
  </Location>
</ArrayOfLocations>

Upvotes: 0

Related Questions