I have an xml file that looks like this
<Root>
<Element1>17890</Element>
<Element2>0001</Element2>
<Element3>123451324A</Element3>
<Element4>1</Element4>
<Element5>ABC</Element5>
<Element6>DEF</Element6>
<Element7>99.10</Element7>
<Element8>GHI</Element8>
<Element9>2014-01-25</Element9>
<Element10>JKL</Element10>
<Element11>737268</Element11>
</Root>
And I have a corresponding class that have all the element names as properties. Let's say I have a collection of all the elements such as
IEnumerable<XElement> elements;
How do I set the property values of the class to the element values from the xml file?
The only thing I have thought of is to loop over elements and make a big switch statement with sections such as
...
case "Element3":
model.Element3 = element.Value;
break;
...
Is there a better solution?
Assuming you already have a class Model with the fields you want to get from the xml:
var elements = XDocument.Load("XMLFile1.xml").Root.Elements();
var model = new Model()
{
Element1 = elements.FirstOrDefault(t => t.Name.LocalName == "Element1"),
Element2 = elements.FirstOrDefault(t => t.Name.LocalName == "Element2"),
};
Another option is to create a dictionary, so you don't need to update your model every time a new tag name is added to the xml file.
var model = new Dictionary<string, string>();
foreach(XElement tag in elements)
{
model[tag.Name.LocalName] = tag.Value;
}
Or using Linq
var model = elements.ToDictionary(e => e.Name.LocalName, e => e.Value);
Finally you can generate a dynamic object directly from the xml. There's an example here: Deserialize XML To Object using Dynamic
Related
I am trying to parse XML document like this:
<root>
<first>1</first>
<second>2</second>
</root>
To structure like this:
class SomeClass
{
...
public string First;
public string Second;
}
but as far as I understood, I can create new object only in select statement, which only can be applied to collection and root element is not a collection.
Of course, I can select fields separately like:
new SomeClass(doc.Element("first").Value, doc.Element("second").Value);
But I'm really interested if is it possible to do it in one LINQ statement (using doc variable only once and creating object inside the LINQ statement)?
In other words: is it possible to create an object not in Select() method?
The root element may not be a collection, but when you parse the xml, your doc variable is a collection of elements, including root element. So you can still use Select:
string xml = #"<root><first>1</first><second>2</second></root>";
var doc = XDocument.Parse(xml);
var collectionOfSomeClass = doc.Elements()
.Select(x => new SomeClass
{ First = x.Element("first").Value,
Second = x.Element("second").Value
});
Please see the XML below. Comparable_Sale is a repeating item. I am using new NewtonSoft’s Json parser and I need to be able to get the value of _Description attribute where the _type attribute equals a particular string value such as “GrossBuildingArea” from the element.
I have used the code below to parse property values with newtsonsoft, but I can’t figure out how to get at the attributes.
<COMPARABLE_SALE PropertySequenceIdentifier="3" ProjectName="Villages of Devinshire" ProjectPhaseIdentifier="1" PropertySalesAmount="132500" SalesPricePerGrossLivingAreaAmount="109.32" DataSourceDescription="FMLS, 5559496;DOM 80" DataSourceVerificationDescription="Tax Recs/2ndGen/Deeds" SalesPriceTotalAdjustmentPositiveIndicator="N" SalePriceTotalAdjustmentAmount="-1500" SalesPriceTotalAdjustmentGrossPercent="1.1" SalePriceTotalAdjustmentNetPercent="1.1" AdjustedSalesPriceAmount="131000">
<SALE_PRICE_ADJUSTMENT _Type="GrossBuildingArea"_Description="1,254"/>
<SALE_PRICE_ADJUSTMENT _Type="BasementArea" _Description="1,254 Sq.Ft."/>
<SALE_PRICE_ADJUSTMENT _Type="BasementFinish" _Description="1rr2ba4o"/>
</COMPARABLE_SALE>
This code gets me to the properties, but I can't see how to get at the attributes.
for each item in jobject.Children(Of JObject)()
For Each [property] In item.Children(Of JProperty)()
If [property].Value.Type = JTokenType.[String] Then
Dim newItem = New xmlRootValues()
newItem.Name = [property].Name
newItem.Value = [property].Value.ToString()
lstValues.Add(newItem)
End If
Next
next
Any help in either (C# or VB.net) would be much appreciated.
Thanks,
Chaos
To work with xml use an xml api like XDocument. Here you have an example in C#:
var xml = #"<COMPARABLE_SALE PropertySequenceIdentifier=""3"" ProjectName=""Villages of Devinshire"" ProjectPhaseIdentifier=""1"" PropertySalesAmount=""132500"" SalesPricePerGrossLivingAreaAmount=""109.32"" DataSourceDescription=""FMLS, 5559496;DOM 80"" DataSourceVerificationDescription=""Tax Recs/2ndGen/Deeds"" SalesPriceTotalAdjustmentPositiveIndicator=""N"" SalePriceTotalAdjustmentAmount=""-1500"" SalesPriceTotalAdjustmentGrossPercent=""1.1"" SalePriceTotalAdjustmentNetPercent=""1.1"" AdjustedSalesPriceAmount=""131000"">
<SALE_PRICE_ADJUSTMENT _Type=""GrossBuildingArea"" _Description=""1,254""/>
<SALE_PRICE_ADJUSTMENT _Type=""BasementArea"" _Description=""1,254 Sq.Ft.""/>
<SALE_PRICE_ADJUSTMENT _Type=""BasementFinish"" _Description=""1rr2ba4o""/>
</COMPARABLE_SALE>";
var xDoc = XDocument.Parse(xml);
var description = xDoc.Root.Elements("SALE_PRICE_ADJUSTMENT")
.First(e => e.Attribute("_Type").Value == "GrossBuildingArea")
.Attribute("_Description")
.Value;
In linq to xml you can also use Descendants and foreach to get all values for same element.
var obj = from item in xDoc.Descendants("SALE_PRICE_ADJUSTMENT")
select new
{
_Descr = item.Attribute("_Description").Value,
_Type = item.Attribute("_Type").Value
};
I am trying to extract some SQL data to XML from a Microsoft Dynamics environment, I am currently using LINQ To XML in C# to read and write to my XML files. One piece of data I need is from a view called SECURITYSUBROLE. Looking at the structure of this view shows that there is a column also named SECURITYSUBROLE. My normal method of extraction has given me this XML.
<SECURITYSUBROLE>
<SECURITYROLE>886301</SECURITYROLE>
<SECURITYSUBROLE>886317</SECURITYSUBROLE>
<VALIDFROM>1900-01-01T00:00:00-06:00</VALIDFROM>
<VALIDFROMTZID>0</VALIDFROMTZID>
<VALIDTO>1900-01-01T00:00:00-06:00</VALIDTO>
<VALIDTOTZID>0</VALIDTOTZID>
<RECVERSION>1</RECVERSION>
<RECID>886317</RECID>
</SECURITYSUBROLE>
When I try to import this data later on, I am getting errors because the parent XML node has the same name as a child node. Here is a snippet of the import method:
XmlReaderSettings settings = new XmlReaderSettings();
settings.CheckCharacters = false;
XmlReader reader = XmlReader.Create(path, settings);
reader.MoveToContent();
int count = 1;
List<XElement> xmlSubset = new List<XElement>();
while (reader.ReadToFollowing(xmlTag))
{
if (count % 1000 == 0)
{
xmlSubset.Add(XElement.Load(reader.ReadSubtree()));
XDocument xmlTemp = new XDocument(new XElement(xmlTag));
foreach (XElement elem in xmlSubset)
{
xmlTemp.Root.Add(elem);
}
xmlSubset = new List<XElement>();
ImportTableByName(connectionString, tableName, xmlTemp);
count = 1;
}
else
{
xmlSubset.Add(XElement.Load(reader.ReadSubtree()));
count++;
}
}
}
It's currently failing on the XmlReader.ReadToFollowing, where it doesn't know where to go next because of the name confusion. So my question has two parts:
1) Is there some better way to be extracting this data other than to XML?
2) Is there a way through LINQ To XML that I can somehow differentiate between the parent and child nodes named exactly the same?
To get the elements (in your case) for SECURITYSUBROLE you can check to see if the element's have children:
XElement root = XElement.Load(path);
var subroles = root.Descendants("SECURITYSUBROLE") // all subroles
.Where(x => !x.HasElements); // only subroles without children
I'm going to suggest a different approach:
1) VS2013 (possibly earlier versions too) has a function to create a class from an XML source. So get one of your XML files and copy the content to your clipboard. Then in a new class file Edit --> Paste Special --> Paste XML as Classes
2) Look into XmlSerialization which will allow you to convert an XML file into an in memory object with a strongly typed class.
XmlSerializer s = new XmlSerializer(yourNewClassTYPE);
TextReader r = new StreamReader(XmlFileLocation);
var dataFromYourXmlAsAStronglyTypedClass = (yourNewlyClassTYPE) s.Deserialize(r);
r.Close();
I'm looking to parse an XML file, and have the ability to search any element.
My XML code looks rather nontraditional (out of my control):
<DiagReport>
<LicensingData>
<ToolVersion>6.3.9431.0</ToolVersion>
<LicensingStatus>SL_LICENSING</LicensingStatus>
</LicensingData>
<Health>
<Result>PASS</Result>
<TamperedItems></TamperedItems>
</Health>
<Genuine>
<ServerProps>GenuineId</ServerProps>
</Genuine>
</DiagReport>
I'd like to load each singular element into a collection, i.e. one collection including ToolVersion, Result, etc. From there, I'd like to iterate through each element/name pair, and depending upon the element, do something different:
if (element.Equals(Result))
//do something with "PASS"
Is this possible to do with LINQ?
You can use LINQ to XML to iterate through all second level elements:
var xdoc = XDocument.Load(#"C:\test.xml");
foreach (var element in xdoc.Element("DiagReport").Elements().Elements())
{
if (element.Name == "Result")
{
string value = element.Value;
}
}
You can use Linq to Xml to query your Xml document, you can search Elements, Attributes and grab what you need.
More information can be found here: Basic Queries (LINQ to XML)
And you can creat simple querys like below.
XElement root = XElement.Parse(File.ReadAllText("C:\\Test.xml"));
var test = new
{
ToolVersion = root.Descendants("ToolVersion").FirstOrDefault().Value,
Result = root.Descendants("Result").FirstOrDefault().Value
};
I've got the following XML, shown in the following image:
But I can't for the life of me, get any code to select the house element between <ArrayOfHouse>.
There will be more than one House element once I've managed to get it to select one, here's my code so far:
// Parse the data as an XML document
XDocument xmlHouseResults = XDocument.Parse(houseSearchResult);
// Select the House elements
XPathNavigator houseNavigator = xmlHouseResults.CreateNavigator();
XPathNodeIterator nodeIter = houseNavigator.Select("/ArrayOfHouse/House");
// Loop through the selected nodes
while (nodeIter.MoveNext())
{
// Show the House id, as taken from the XML document
MessageBox.Show(nodeIter.Current.SelectSingleNode("house_id").ToString());
}
I'm getting the stream of XML, because I have managed to show the data in the MessageBox shown above, but I can't get to the individual houses.
You can select the House nodes like this:
var houses = XDocument.Parse(houseSearchResult).Descendants("House");
foreach(var house in houses)
{
var id = house.Element("house_id");
var location = house.Element("location");
}
Or you can use Select to directly get a strongly typed object:
var houses = XDocument.Parse(houseSearchResult)
.Descendants("House")
.Select(x => new House
{
Id = x.Element("house_id"),
Location = x.Element("location")
});
This assumes that there exists a class House with the properties Id and Location.
Also, please be sure to think about the suggestion by Thomas Levesque to use XML serialization.
With XPath you would need to use an XmlNamespaceManager, however as you have an XDocument you could simply use the LINQ to XML axis methods e.g.
XNamespace df = XmlHouseResults.Root.Name.Namespace;
foreach (XElement house in XmlHouseResults.Descendants("df" + "House"))
{
MessageBox.Show((string)house.Element("df" + "house_id"));
}