I have not used XML for very long and need to extract the useful information from an XML response. If there are 2 tags that are the same but have a different name e.g
<lst name = "stack">
<str>Ola</str>
<lst name = "overflow">
<str>Hello</str>
</lst>
</lst>
How would I extract the contents of the tag with name="overflow"?
You can use LINQ To XML:
var result = XDocument.Parse(xml)
.Descendants("lst")
.Where(e => (string) e.Attribute("name") == "overflow")
.Descendants("str")
.Select(x => x.Value)
.FirstOrDefault();
Try this to start:
XPathDocument docNav = new XPathDocument(pathName);
XPathNavigator nav = docNav.CreateNavigator();
XmlNamespaceManager ns = new XmlNamespaceManager(nav.NameTable);
string val = nav.SelectSingleNode(#"/lst/lst[#name='overflow']/str")
These are good resources for simple XPath navigation and .NET XML Parsing:
http://www.w3schools.com/xpath/
http://www.codeproject.com/Articles/52079/Using-XPathNavigator-in-C
You may use the System.Xml.Linq namespace:
var xDoc = XDocument.Parse(xml);
var result = xDoc.Descendants()
.Where(d =>
d.Name == "lst" &&
d.Attributes("name").FirstOrDefault()!=null &&
d.Attributes("name").FirstOrDefault().Value == "overflow")
.FirstOrDefault();
User Linq to xml
var xmlFile = XDocument.Load(someFile);
var query = from item in xmlFile.Descendants("childobject")
where !String.IsNullOrEmpty(item.Attribute("using")
select new
{
AttributeValue = item.Attribute("using").Value
};
You can do it with LINQ to XML:
var doc = XDocument.Load("YourXMLPath.xml");
var content = doc
.Element("lst")
.Elements("lst")
.Where(e=>((string) e.Attribute("name") ?? "")=="overflow")
.Select(e=>e.Element("str").InnerText())
.FirstOrDefault();
LINQ to XML in System.Xml.Linq namespace.
const string xml = #"<lst name = ""stack""><str>Ola</str><lst name = ""overflow""><str>Hello</str></lst></lst>";
XDocument doc = XDocument.Parse(xml);
IEnumerable<XElement> overflow = doc.Root.Elements("lst").Where(x => (string) x.Attribute("name") == "overflow");
XElement firstOverflow = overflow.FirstOrDefault();
string value = firstOverflow.Descendants("str").FirstOrDefault(x => x.Value);
Related
This is a portion of XML I'm trying to parse
<BRTHDATES>
<BRTHDATE value="5/1/1963" code="B"/>
</BRTHDATES>
var birthdates = xmlDoc.XPathSelectElements("/INDV/PERSON/BRTHDATES").Elements().Where(e => e.Name == "BRTHDATE");
xe = birthdates.Elements().Where(e => e.Name == "BRTHDATE");
bbs = from b in birthdates
select new
{
Birthdays = b.FirstAttribute.Value,
Code = b?.Value
};
var status = birthdates.Elements().Where(e => e.Name.LocalName == "BRTHDATE").Single().Value;
When I try to get "Value" from the Element I get an empty string. I can't get anything for the "code" attribute.
It sure seems like this should be a lot easier...
You can try below code. I've already tested through a test project and got the require value.
string personBirthday = string.Empty;
string soapResult = #"<?xml version=""1.0"" encoding=""utf - 8"" ?><INDV> <PERSON> <BRTHDATES><BRTHDATE value = ""5/1/1963"" code = ""B"" /> </BRTHDATES></PERSON></INDV> ";
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(soapResult));
XmlNodeList person = doc.GetElementsByTagName("BRTHDATES");
if (person[0].ChildNodes.Count > 0)
{
foreach (XmlNode item in person[0].ChildNodes)
{
if (item.Name.Trim().Equals("BRTHDATE"))
{
personBirthday = !string.IsNullOrEmpty(item.Attributes[0].Value) ? item.Attributes[0].Value.Trim() : string.Empty;
}
}
}
Here is the solution
You can select specific Element from a Xml. Just try below sample code
XmlNodeList generalTabNodeList = xmlDocument.SelectNodes("/INDV/PERSON/BRTHDATES");
foreach (XmlNode node in generalTabNodeList)
{
if (node.ChildNodes.Count > 0)
{
string birthdate = !string.IsNullOrEmpty(node.ChildNodes[0].ToString()) ? node.ChildNodes[2].InnerText.ToString() : string.Empty;
}
}
I can't quite follow what you are trying to do, but, this should get you going:
For an XML file that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<INDV>
<PERSON>
<BRTHDATES>
<BRTHDATE value="5/1/1963" code="B"/>
</BRTHDATES>
</PERSON>
</INDV>
(Note, this is an entire XML document - one that matches your code, not just the snippet you provided (that doesn't match your code))
This code will pick out the value and code attributes:
using (var xmlStream = new FileStream("Test.xml", FileMode.Open))
{
XDocument xmlDocument = XDocument.Load(xmlStream);
var birthDateElements = xmlDocument.XPathSelectElements("/INDV/PERSON/BRTHDATES/BRTHDATE");
var birthDateElement = birthDateElements.FirstOrDefault();
if (birthDateElement != null)
{
var attributes = birthDateElement.Attributes();
var valueAttribute = attributes.Where(a => a.Name == "value");
var codeAttribute = attributes.Where(a => a.Name == "code");
}
}
You can play around with this code to figure out what you want to do. Whatever you do, don't pick out attributes by position, pick them out by name.
I would like to convert a text which contains tag three into a list. Example :
var raw = #"<root><group><tag1>text1</tag1><tag3>text3</tag3</group><tag2>text2</tag2></root>";
And I need to have something like this :
Dictionary<string,string> dicTags = new Dictionary<string,string>();
dicTags["tag1"] = "text1";
dicTags["tag3"] = "text3";
dicTags["tag2"] = "text2";
Here is a example (working but not with this multiple three). The added tag <group></group> make it fail :
var raw = #"<root><group><tag1>text1</tag1><tag3>text3</tag3></group><tag2>text2</tag2></root>";
var doc = XDocument.Parse(raw);
var result = doc.Root.Elements().ToDictionary(e => (string)e.Name.LocalName, e => (string)e);
foreach(var kv in result){
Console.WriteLine("Key: {0}, Value: {1}", kv.Key, kv.Value);
}
After reading your comments I came up with this:
var raw =
#"<root><group><tag1>text1</tag1><tag3>text3</tag3></group><tag2>text2</tag2></root>";
var doc = XDocument.Parse(raw);
var result = doc.Descendants()
.Where(el => !el.HasElements)
.ToDictionary(k => k.Name, v => v.Value);
If you want to ignore tags like <tag4><tag4> (no descendants and empty value) then change the condition to the following:
.Where(el => !el.HasElements && !string.IsNullOrEmpty(el.Value))
Use XMLDocument load your string with LoadXML() method than read your XMLstring.
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xmlstring);
foreach(XmlNode node in doc.DocumentElement.ChildNodes){
string text = node.InnerText;
}
This is how the XML file could look:
<data>
<subdata>
<datatype id="1" name="data1">
<xdim>2</xdim>
<ydim>1</ydim>
</datatype>
<datatype id="2" name="data2">
<xdim>3</xdim>
<ydim>4</ydim>
</datatype>
</subdata>
</data>
Now, i want the following:
A list(string) with all datatype id's like "1" & "2" in the preview above
A list(string) with all the < xdim > stuff like "2" & "3" above
A list(string) with all the < ydim > stuff like "1" & "4" above
Are there easy methods built in in C# for stuff like this? Or could anyone help me with this question?
Jonas
You can use Descendents method.
This method reads all the child nodes even the nested ones where node name matches with specified string.
var Idstring = MyXml.Descendants("datatype").Select (x=>x.Attribute("Id")).ToList();
var xdimstring = MyXml.Descendants("xdim").Select (x=>x.Value).ToList();
var ydimstring = MyXml.Descendants("ydim").Select (x=>x.Value).ToList();
To Appease your curiosity :)
This how you can get nodes from specifically subdata node.
var Idstring = MyXml.Descendants("Subdata").Descendants("datatype").Select (x=>x.Attribute("Id")).ToList();
var xdimstring = MyXml.Descendants("Subdata").Descendants("xdim").Select (x=>x.Value).ToList();
var ydimstring = MyXml.Descendants("Subdata").Descendants("ydim").Select (x=>x.Value).ToList();
Now let say you have Multiple subdata and you want to read nodes only from first one...Simply use First linq extension method
var Idstring = MyXml.Descendants("Subdata").First().Descendants("datatype").Select (x=>x.Attribute("Id")).ToList();
This works and is fairly neat and simple:
string xml =
#"<data>
<subdata>
<datatype id=""1"" name=""data1"">
<xdim>2</xdim>
<ydim>1</ydim>
</datatype>
<datatype id=""2"" name=""data2"">
<xdim>3</xdim>
<ydim>4</ydim>
</datatype>
</subdata>
</data>";
var xelem = XElement.Parse(xml);
var allIDs = xelem
.Descendants()
.Where (x => x.Attribute("id") != null)
.Select (x => x.Attribute("id").Value)
.ToList();
var allXdims = xelem
.XPathSelectElements("//xdim")
.Select (x => x.Value)
.ToList();
var allYdims = xelem
.XPathSelectElements("//ydim")
.Select (x => x.Value)
.ToList();
Obviously the part at the start is just getting the XML into an XElement. You might want to do this with:
var xelem = XElement.Load(myXmlLocation);
instead.
The easiest way to convert classes to and from XML in C# is XML Serialization
For your example, you could create a class with member variables corresponding to the tags in your XML. When you want to create your XML file, you serialize the class to an XML File.
When your want to read back the information, you deserialize the contents of the XML file back to the class you created.
Here's a more comprehensive article: https://msdn.microsoft.com/en-us/library/58a18dwa(v=vs.110).aspx
You can use:
XDocument doc = XDocument.Load(#"..\myfile.xml");
to load your file in a XDocument object.
Then use XDocument methods to create string lists of the required id values:
var ids = (from a in doc.Descendants("subdata").Elements().Attributes("id")
select a.Value).ToList();
var xids = (from e in doc.Descendants("datatype").Elements("xdim")
select e.Value).ToList();
var yids = (from e in doc.Descendants("datatype").Elements("ydim")
select e.Value).ToList();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(
"<data><subdata><datatype id=\"1\" name=\"data1\"><xdim>2</xdim><ydim>1</ydim></datatype><datatype id=\"2\" name=\"data2\"><xdim>3</xdim><ydim>4</ydim></datatype></subdata></data>");
var nodes = xmlDocument.SelectNodes("//datatype");
var first = new List<string>();
var Second = new List<string>();
var third = new List<string>();
foreach (XmlNode node in nodes)
{
first.Add(node.Attributes["id"].Value);
}
nodes = xmlDocument.SelectNodes("//xdim");
foreach (XmlNode node in nodes)
{
Second.Add(node.InnerText);
}
nodes = xmlDocument.SelectNodes("//ydim");
foreach (XmlNode node in nodes)
{
third.Add(node.InnerText);
}
Try something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication81
{
class Program
{
static void Main(string[] args)
{
string xml =
"<data>" +
"<subdata>" +
"<datatype id=\"1\" name=\"data1\">" +
"<xdim>2</xdim>" +
"<ydim>1</ydim>" +
"</datatype>" +
"<datatype id=\"2\" name=\"data2\">" +
"<xdim>3</xdim>" +
"<ydim>4</ydim>" +
"</datatype>" +
"</subdata>" +
"</data>";
XElement data = XElement.Parse(xml);
var results = data.Descendants("subdata").Elements()
.GroupBy(x => x.Name.LocalName)
.Select(x => new
{
name = x.Key,
value = x.Select(y => (string)y).ToList(),
attributes = x.Attributes()
.Select(y => new {name = y.Name.LocalName, y.Value})
.GroupBy(y => y.name, z => z.Value)
.ToDictionary(y => y.Key, z => z.ToList())
}).ToList();
}
}
}
This is my current attempt to transform key value pair xml to a C# dictionary:
const string xml = #"<KVP><Id>642</Id><StartDate>2012-03-01T00:00:00</StartDate></KVP>";
var xdoc = XDocument.Parse(xml);
if (xdoc.Root != null)
{
var map = xdoc.Root.Elements()
.ToDictionary(a => a.Name.LocalName,
a => a.Value.Trim());
}
Can this be improved?
Given the following XML:
<platforms>
<platform>
<id>1</id>
<price>2.99</price>
</platform>
</platforms>
How can I select the "platform" element as an XElement object based on the child element "id" having a value of "1"?
I have got this far:
XDocument xPlatformXml = new XDocument();
XElement xel = xPlatformXml.Element("platforms").Elements("platform").Where(x => x.Value == "1").SingleOrDefault();
But this is looking for the value to be in "platform" element rather than "id".
XDocument xPlatformXml = new XDocument();
XElement xel = xPlatformXml.Element("platforms")
.Elements("platform")
.Where(x => x.Element("id").Value == "1")
.SingleOrDefault();
Or using XElement to int conversion:
XDocument xPlatformXml = new XDocument();
XElement xel = xPlatformXml.Element("platforms")
.Elements("platform")
.Where(x => (int)x.Element("id") == 1)
.SingleOrDefault();