views:

26

answers:

3

I have an XML document that has a collection of objects. Each object has a key/value pair of label and value. I am trying to convert this into a DataSet, but when I do ds.ReadXml(xmlFile), then it creates two columns: label and value.

What I would like is to have a column for each "label" and the value to be part of the row. here is my sample of the XML:

<responses>
  <response>
    <properties id="1" Form="Account Request" Date="Tuesday, March 16, 2010 5:04:26 PM" Confirmation="True" />
    <fields>
      <field>
        <label>Name</label>
        <value>John</value>
      </field>
      <field>
        <label>Email</label>
        <value>[email protected]</value>
      </field>
      <field>
        <label>Website</label>
        <value>http://domain1.com&lt;/value&gt;
      </field>
      <field>
        <label>Phone</label>
        <value>999-999-9999</value>
      </field>
      <field>
        <label>Place of Birth</label>
        <value>Earth</value>
      </field>
      <field>
        <label>Misc</label>
        <value>Misc</value>
      </field>
      <field>
        <label>Comments</label>
        <value />
      </field>
      <field>
        <label>Agree to Terms?</label>
        <value>True</value>
      </field>
    </fields>
  </response>
  <response>
    <properties id="2" Form="Account Request" Date="Tuesday, March 17, 2010 5:04:26 PM" Confirmation="True" />
    <fields>
      <field>
        <label>Name</label>
        <value>John2</value>
      </field>
      <field>
        <label>Email</label>
        <value>[email protected]</value>
      </field>
      <field>
        <label>Website</label>
        <value>http://domain2.com&lt;/value&gt;
      </field>
      <field>
        <label>Phone</label>
        <value>999-999-9999</value>
      </field>
      <field>
        <label>Place of Birth</label>
        <value>Earth</value>
      </field>
      <field>
        <label>Misc</label>
        <value>Misc</value>
      </field>
      <field>
        <label>Comments</label>
        <value />
      </field>
      <field>
        <label>Agree to Terms?</label>
        <value>True</value>
      </field>
    </fields>
  </response>
  <response>
    <properties id="3" Form="Account Request" Date="Tuesday, March 18, 2010 5:04:26 PM" Confirmation="True" />
    <fields>
      <field>
        <label>Name</label>
        <value>John3</value>
      </field>
      <field>
        <label>Email</label>
        <value>[email protected]</value>
      </field>
      <field>
        <label>Website</label>
        <value>http://domain3.com&lt;/value&gt;
      </field>
      <field>
        <label>Phone</label>
        <value>999-999-9999</value>
      </field>
      <field>
        <label>Place of Birth</label>
        <value>Earth</value>
      </field>
      <field>
        <label>Misc</label>
        <value>Misc</value>
      </field>
      <field>
        <label>Comments</label>
        <value />
      </field>
      <field>
        <label>Agree to Terms?</label>
        <value>True</value>
      </field>
    </fields>
  </response>
  <response>
    <properties id="4" Form="Account Request" Date="Tuesday, March 19, 2010 5:04:26 PM" Confirmation="True" />
    <fields>
      <field>
        <label>Name</label>
        <value>John</value>
      </field>
      <field>
        <label>Email</label>
        <value>[email protected]</value>
      </field>
      <field>
        <label>Website</label>
        <value>http://domain4.com&lt;/value&gt;
      </field>
      <field>
        <label>Phone</label>
        <value>999-999-9999</value>
      </field>
      <field>
        <label>Place of Birth</label>
        <value>Earth</value>
      </field>
      <field>
        <label>Misc</label>
        <value>Misc</value>
      </field>
      <field>
        <label>Comments</label>
        <value />
      </field>
      <field>
        <label>Agree to Terms?</label>
        <value>True</value>
      </field>
    </fields>
  </response>
</responses>

How would I convert this to a DataSet so that I can load it into a gridview with the columns: Name, Email, Website, Phone, Place of Birth, Misc, Comments, and Agree to Terms?

Then row 1 would be: John, [email protected], http://domain1.com, 999-999-9999, Earth, Misc, , True

How can I do this with the XML provided?

+1  A: 

You're going to have to transform your data in order to use it the way you want. As you've seen, you have a bad structure.

I suggest that you create an empty dataset in Visual Studio (from Add->New Item), then set it to look the way you'd like it to look. Write some code to add a little test data, then write it to a file using DataSet.WriteXml. That will show you what your proposed structure would look like.

I then recommend that you use LINQ to XML to transform your input XML into the new format.


Here's an example of using LINQ to XML to transform your data:

public static void TransformIt(TextWriter output)
{
    var inputDocument = XDocument.Parse(INPUT_XML);
    if (inputDocument.Root == null)
    {
        return;
    }

    var doc = new XDocument(
        new XElement(
            "responses",
            from response in inputDocument.Root.Elements()
            select new XElement(
                "response",
                from lv in GetResponseLabels(response)
                select MakeResponse(lv.Label, lv.Value))));

    var settings = new XmlWriterSettings
    {
        Encoding = Encoding.UTF8,
        Indent = true,
    };
    using (var writer = XmlWriter.Create(output, settings))
    {
        if (writer == null)
        {
            return;
        }

        doc.WriteTo(writer);
    }
}

private static XElement MakeResponse(string label, string value)
{
    var trimmedLabel = label.Replace(" ", String.Empty).Replace("?", String.Empty);
    return new XElement(trimmedLabel, value);
}

private static IEnumerable<LabelAndValue> GetResponseLabels(XContainer response)
{
    var fieldsElement = response.Element("fields");
    if (fieldsElement == null)
    {
        return null;
    }

    return from field in fieldsElement.Elements("field")
           let valueElement = field.Element("value")
           let labelElement = field.Element("label")
           select new LabelAndValue
           {
               Label = labelElement == null ? "Unknown" : labelElement.Value,
               Value = valueElement == null ? null : valueElement.Value
           };
}

private struct LabelAndValue
{
    public string Label { get; set; }
    public string Value { get; set; }
}
John Saunders
A: 

I would iterate through the XML and depending on how you iterate (Linq is most flexible), create a new object(datatable, for instance) with your data described in the way you need.

TheGeekYouNeed
A: 

I ended up changing the approach a bit and did this (pivots):

DataRow dr = dt.NewRow();

//TRANSFORM RESPONSE LABELS INTO COLUMNS
foreach (XmlNode fieldNode in currentXml.SelectNodes("response/fields/field"))
{
    string label = fieldNode.SelectSingleNode("label").InnerText ?? "Unknown";
    string value = fieldNode.SelectSingleNode("value").InnerText;
    //CHECK IF ARBITRARY LABEL WAS ADDED BEFORE
    if (!dt.Columns.Contains(label))
    {
        //CREATE COLUMN FOR NEW LABEL
        dt.Columns.Add(label);
    }
    dr[label] = value;
}
dt.Rows.Add(dr);
}
ds.Tables.Add(dt);
TruMan1