Below is an example of my starting xml and the xml result i'm trying to achieve. What i'm trying to figure out is not how to create the desired xml but how to grab all matches nodes that have the same starting value. For example below, you'll see the xml has apples, there are two nodes with apples. I want to find all those nodes and then create a custom xml afterwards.
How do you loop for all nodes of an xml and then find all results on that same node level that have matching values?
<?xml version="1.0"?>
<results>
<result>
<fruit>apples</fruit>
<price>0</price>
</result>
<result>
<fruit>pears</fruit>
<price>1</price>
</result>
<result>
<fruit>apples</fruit>
<price>2</price>
</result>
</results>
<?xml version="1.0"?>
<results>
<result>
<fruit>apples</fruit>
<prices>
<price>0</price>
<price>2</price>
</prices>
</result>
<result>
<fruit>pears</fruit>
<prices>
<price>1</price>
</prices>
</result>
</results>
This VB code creates the desired output. It selects as results and orders them by fruit value, and then a simple loop to create the desired output.
Dim xe As XElement
' to load from a file
' Dim yourpath As String = "your path here"
'xe = XElement.Load(yourpath)
' for testing
xe = <results>
<result>
<fruit>apples</fruit>
<price>0</price>
</result>
<result>
<fruit>pears</fruit>
<price>1</price>
</result>
<result>
<fruit>apples</fruit>
<price>2</price>
</result>
</results>
Dim oxe As XElement = <results></results>
Dim rsltproto As XElement = <result>
<fruit></fruit>
<prices></prices>
</result>
Dim rslt As XElement
Dim fruit As String
'select all <result> order by fruit values
Dim allfruits As IEnumerable(Of XElement) = xe...<result>.OrderBy(Function(el) el.<fruit>.Value)
'simple loop to create new XML
For Each el As XElement In allfruits
If el.<fruit>.Value <> fruit Then
If rslt IsNot Nothing Then
oxe.Add(rslt)
End If
fruit = el.<fruit>.Value
rslt = New XElement(rsltproto)
rslt.<fruit>.Value = fruit
End If
rslt.<prices>.LastOrDefault.Add(el.<price>)
Next
If rslt IsNot Nothing Then
oxe.Add(rslt)
End If
' to save file
' xe.Save(yourpath)
This is a simple change to Timon's answer above
var doc = XDocument.Load(#"<path>\test.xml");
var fruitPrices = doc.XPathSelectElements("results/result")
.Select(d => new {
Fruit = d.Element("fruit").Value,
// TODO: parse to int if required
Price = d.Element("price").Value
})
.GroupBy(f => f.Fruit)
.Select(g => new {Fruit = g.Key, Prices = g.Select(x => x.Price)})
.ToList();
In LinqPad this gives
You can accomplish this by using XDocument. Do not forget to import.
using System.Xml.Linq;
using System.Xml.XPath;
load document
XDocument doc = XDocument.Load("C:\\t\\My File2.txt");
Select all result elements
Group by fruit value for example 3 apples 1 pears
Put the result elements from group and add them into an array
Create list that contains the array with the result elements
List<XElement[]> multipleElements = doc
.XPathSelectElements("results/result")
.GroupBy(result => result.Element("fruit").Value)
.Select(groupContent => groupContent.ToArray())
.ToList();
=========== result ===================
List [0] { apples,apples}
List [1] { pear }
XML I tested on:
<?xml version="1.0"?>
<results>
<result>
<fruit>apples</fruit>
<price>0</price>
</result>
<result>
<fruit>pears</fruit>
<price>1</price>
</result>
<result>
<fruit>apples</fruit>
<price>2</price>
</result>
</results>
Related
I have an XML document I am trying to query with LINQ to XML. The XML is ..
<?xml version="1.0" encoding="UTF-8"?>
<response>
<operation>
<authentication>
<username>redacted</username>
<isauthenticated>true</<isauthenticated>>
</authentication>
<result>
<status>success</status>
<other-elements></<other-elements>
<other-elements></<other-elements>
<other-elements>
<other-sub-elements></other-sub-elements>
<other-sub-elements></other-sub-elements>
</<other-elements>
</result>
</operation>
</response>
I am trying to read the value of the node <status> to determine if the API call was successful. I am having trouble putting together the LINQ syntax necessary to grab the value of the <status node. I thought I could use XPath syntax as such to grab the value.
XDocument xml = XDocument.Parse(xmlResponse);
string returnValue = = xml.Descendants("result/status").Select(x => x.Name).ToString();
However, I am getting the following error ..
The '/' character, hexadecimal value 0x2F, cannot be included in a name.
Try this code:
XDocument xdoc = XDocument.Parse(#"
<response>
<operation>
<authentication>
<username>redacted</username>
<isauthenticated>true</isauthenticated>
</authentication>
<result>
<status>success</status>
</result>
</operation>
</response>
");
Boolean isSuccess;
String s = xdoc.Element("response").Element("operation").Element("result").Element("status").Value;
isSuccess = s == "success";
It gets the value of the status element and checks whether it is equal to a specific value; in this case, isSuccess will be true.
The LINQ-to-XML methods Elements() and Descendants() only deal with single names, not xpath-like paths. If you want to give an xpath expression, use the xpath extensions.
// using System.Xml.Linq;
var status = (string)xml.XPathSelectElement("//result/status");
Otherwise you need to build up an equivalent query using the methods correctly.
var status = (string)xml.Descendants("result").Elements("status").FirstOrDefault();
This is my XML structure:
<root>
<report>
<results>
<result>
<name> name 1 </name>
</result>
<result>
<name> name 2 </name>
</result>
</results>
</report>
</root>
I want to read the information within result and create an object to set it's attribute to the value. This is my code to do this:
Read from XML:
XDocument rootDocument = XDocument.Load(filePath)
var vul = from r in rootDocument.Descendants("result")
select new
{
Name = r.Element("name").Value
};
Create new object:
foreach(var r in vul)
{
Object a = new Object()
{
Name = r.Name
};
}
The Problem is that this produces a NullReferenceException when calling
rootDocument.Descendants("result")
Thank you in advance!
I am guessing that your select is trying to access an element that does not exist.
For example there is a missing name element from your original xml:
e.g. This xml will fail
<root>
<report>
<results>
<result>
<name> name 1 </name>
</result>
<result>
<name> name 2 </name>
</result>
<result>
<surname> name 2 </surname> //will fail when loop gets here
</result>
</results>
</report>
</root>
If it is possible that name element will not always exist you can update the code to handle null
var vul = from r in rootDocument.Descendants("result")
select new
{
Name = r.Element("name")?.Value, //this will return null
};
..as r.Name can be null you need to account for this in the for loop.
foreach (var r in vul)
{
string Name = r.Name ?? string.empty;
}
I found the cause of this Error. I don't know why but calling the value of an element causes it. I changed
Name = r.Element("name").Value
to
Name= r.Element("name")
I have an array which its structure is:
<data>
<id></id>
<list></list>
</data>
And I want to write an array to list node
<data>
<id></id>
<list>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</list>
</data>
Then, delete an element in that array:
<data>
<id></id>
<list>
<item>1</item>
<item>3</item>
<item>4</item>
</list>
</data>
Next, modify that array:
<data>
<id></id>
<list>
<item>1</item>
<item>2</item>
<item>3</item>
</list>
</data>
How to write / read/ modify an array which inside the xml array?
Any ideas?
To read:
XDocument doc = XDocument.Load("data.xml");
var listPath = from elements in doc.Elements("data").Elements("list") select elements;
foreach (var docItem in listPath)
{
var itemVar= Convert.ToInt32(doc .Element("item").Value);
}
To write:
XDocument doc = XDocument.Load("data.xml");
var list = doc.Root.Element("list");
list.Add(new XElement("item", value));
To edit:
XDocument doc = XDocument.Load("data.xml");
var list = doc.Root.Element("list");
list.Element("item").Value = newValue;
any powerful and faster solution?
I have a snippet of an xml file that looks like this:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="wa_xml2html.xsl"?>
<computeraudit>
<title>Computer Audit :: 11/13/2012 10:43:22 AM</title>
<category title="Loaded Modules">
<subcategory title="">
<recordset title="">
<fieldname>Name</fieldname>
<fieldname>Version</fieldname>
<fieldname>Modified</fieldname>
<fieldname>Manufacturer</fieldname>
<datarow>
<fieldvalue>XmlLite.dll</fieldvalue>
<fieldvalue>1.3.1000.0</fieldvalue>
<fieldvalue>7/13/2009 8:16:21 PM</fieldvalue>
<fieldvalue>Microsoft Corporation</fieldvalue>
</datarow>
<datarow>
<fieldvalue>zip.dll</fieldvalue>
<fieldvalue>6.0.250.6</fieldvalue>
<fieldvalue>5/25/2011 8:30:12 AM</fieldvalue>
<fieldvalue>Sun Microsystems, Inc.</fieldvalue>
</datarow>
</recordset>
</subcategory>
</category>
</computeraudit>
I'm trying to parse it with C# XPath and get each of the fieldvalue element values.
The c# code looks like this:
XPathNavigator nav;
XPathDocument docNav;
XPathNodeIterator NodeIter;
String strExpression;
docNav = new XPathDocument(#"C:\TEMP\WinAudit\test.xml");
nav = docNav.CreateNavigator();
strExpression = "/computeraudit/category[#title=\"Loaded Modules\"]/subcategory/recordset/datarow";
NodeIter = nav.Select(strExpression);
while (NodeIter.MoveNext())
{
Console.WriteLine(NodeIter.Current.Value);
}
Console.ReadLine();
It returns one line for each datarow node with all of the fieldvalue element values concatenated together.
How do I return each of the distinct values for each fieldvalue element?
Change your XPath expression to:
"/computeraudit/category[#title=\"Loaded Modules\"]/subcategory/recordset/datarow/fieldvalue"
Your selector is only selecting the datarow elements.
Try strExpression = "/computeraudit/category[#title=\"Loaded Modules\"]/subcategory/recordset/datarow/fieldvalue";
I have a whole XML document in a String which i need to convert to a XML document and parse tags in the document
This code sample is taken from csharp-examples.net, written by Jan Slama:
To find nodes in an XML file you can use XPath expressions. Method XmlNode.SelectNodes returns a list of nodes selected by the XPath string. Method XmlNode.SelectSingleNode finds the first node that matches the XPath string.
XML:
<Names>
<Name>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
</Name>
<Name>
<FirstName>James</FirstName>
<LastName>White</LastName>
</Name>
</Names>
CODE:
XmlDocument xml = new XmlDocument();
xml.LoadXml(myXmlString); // suppose that myXmlString contains "<Names>...</Names>"
XmlNodeList xnList = xml.SelectNodes("/Names/Name");
foreach (XmlNode xn in xnList)
{
string firstName = xn["FirstName"].InnerText;
string lastName = xn["LastName"].InnerText;
Console.WriteLine("Name: {0} {1}", firstName, lastName);
}
Using Linq to xml
Add a reference to System.Xml.Linq
and use
XDocument.Parse(string xmlString)
Edit: Sample follows, xml data (TestConfig.xml)..
<?xml version="1.0"?>
<Tests>
<Test TestId="0001" TestType="CMD">
<Name>Convert number to string</Name>
<CommandLine>Examp1.EXE</CommandLine>
<Input>1</Input>
<Output>One</Output>
</Test>
<Test TestId="0002" TestType="CMD">
<Name>Find succeeding characters</Name>
<CommandLine>Examp2.EXE</CommandLine>
<Input>abc</Input>
<Output>def</Output>
</Test>
<Test TestId="0003" TestType="GUI">
<Name>Convert multiple numbers to strings</Name>
<CommandLine>Examp2.EXE /Verbose</CommandLine>
<Input>123</Input>
<Output>One Two Three</Output>
</Test>
<Test TestId="0004" TestType="GUI">
<Name>Find correlated key</Name>
<CommandLine>Examp3.EXE</CommandLine>
<Input>a1</Input>
<Output>b1</Output>
</Test>
<Test TestId="0005" TestType="GUI">
<Name>Count characters</Name>
<CommandLine>FinalExamp.EXE</CommandLine>
<Input>This is a test</Input>
<Output>14</Output>
</Test>
<Test TestId="0006" TestType="GUI">
<Name>Another Test</Name>
<CommandLine>Examp2.EXE</CommandLine>
<Input>Test Input</Input>
<Output>10</Output>
</Test>
</Tests>
C# usage...
XElement root = XElement.Load("TestConfig.xml");
IEnumerable<XElement> tests =
from el in root.Elements("Test")
where (string)el.Element("CommandLine") == "Examp2.EXE"
select el;
foreach (XElement el in tests)
Console.WriteLine((string)el.Attribute("TestId"));
This code produces the following output:
0002
0006
Depending on what document type you want you can use XmlDocument.LoadXml or XDocument.Load.
Try this code:
var myXmlDocument = new XmlDocument();
myXmlDocument.LoadXml(theString);