Reputation: 36158
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</value>
</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</value>
</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</value>
</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</value>
</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?
Upvotes: 1
Views: 870
Reputation: 36158
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);
Upvotes: 0
Reputation: 161831
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; }
}
Upvotes: 1
Reputation: 7539
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.
Upvotes: 0