XML parsing using XElement and child contents - c#

I have a function that returns the value from a specific tag in an XML document:
XElement elem = XElement.Parse(xml_string);
string ret = elem.Element(key).Value.ToString();
return ret;
I'm trying to figure out how to create another method that returns the full string contents contained within a tag, including child tags and child values.
i.e. if I have:
<foo>
Hello
<child1>val1</child1>
<child2>val2</child2>
</foo>
The method above properly returns 'Hello', but what I want is another method that returns:
Hello<child1>val1</child1><child2>val2</child2>

The easiest option is to spin through the collection returned by XElement.Nodes() and concatenate the XNode.ToString() values for all of those nodes. If you don't want it formatted (and it sounds like you don't), call XNode.ToString(SaveOptions.DisableFormatting)

Related

Inserting XML nodes and inner nodes to an existing XML document in C#

Currently I have a working C# program that works as follows:
Accept .xls template with values (xls is manually created by user)
Save the values (matching fields) to the database
Convert and write .xls to XML. Please see below sample output:
Existing XML Structure
Now, what I want to do is:
Read the existing xml (the created xml)
Insert another set of nodes and subnodes (ReleaseLine and sub nodes). It must accept multiple ReleaseLine.
Save/create the new xml with appended nodes. Please see below output:
This is what I'm looking for:
My existing C# program is simple but the XML nodes and hierarchy is bloody deep. I just created the C# code using new XElement method and passing values for each nodes. Then I simply use xmlDocument.Save() method to write the xml.
[Existing XML Program][3]
To add nodes or append content in existing xml-data I´d use Linq to XML.
XElement xml = XElement.Load("file.xml");
xml.Add( new XElement("uberNode",
new XElement("childNode", content),
new XElement("anotherChildNode", content)));
xml.Save("file.xml");
Here are some other related solutions.
Add to specific node (with example):
Following exisiting XML-data:
`<Names>
<Name>
<prename>John</prename>
<lastname>Snow</lastname>
</Name>
<Name>
<prename>Harry</prename>
<lastname>Harry</lastname>
</Name>
</Names>`
Now I want to add an "age"-tag before the first "prename"-tag and a "family"-tag after the first "lastname"-tag.
XElement xml = XElement.Load("file.xml");
var childrens = xml.DescendantsAndSelf().ToArray();
var first_prename = childrens[2];
var first_lastname = childrens[3];
Console.WriteLine(childrens[0]); //prints out the whole content
first_prename.AddBeforeSelf(new XElement("age", 22));
first_lastname.AddAfterSelf(new XElement("family", new XElement("mother", "paula"), new XElement("father", "paul")));
xml.Save("file.xml");
Outcome:
`<Names>
<Name>
<age>22</age>
<prename>John</prename>
<lastname>Snow</lastname>
<family>
<mother>paula</mother>
<father>paul</father>
</family>
</Name>
<Name>
<prename>Harry</prename>
<lastname>Harry</lastname>
</Name>
</Names>`
I was facing the problem and Linq gave me the easiest way to accomplish that!
There are also other similar way e.g. here. But I tried a bit more and DescendantsAndSelf() made it easier for me to go through.
I found an answer to my question, here is the link http://www.xmlplease.com/add-xml-linq
Using XPathSelectElement method, I was able to find the right node and appended new block of XElement.

Extract data from a XML string using linq vs xmlDocument

I have done the below many times using a xmlDocument approach, but I wanted to use the more powerful linq to xml approach.
However, I seem to have run into a wall.
I am getting data back from a restful API from twillio / crmText.
Here is a link to their site where their docs live:
http://crmtext.com/api/docs
Here is my XML string:
<response op="getcustomerinfo" status="200" message="ok" version="1.0">
<customer>
<totalMsg>3</totalMsg>
<custId>9008281</custId>
<custName></custName>
<timestamp>2015-04-30 16:17:19</timestamp>
<optinStatus>3</optinStatus>
<custMobile>6185551212</custMobile>
<subacct>1st Choice Courier</subacct>
</customer>
</response>
I need to find out the optinStatus. It should return 3.
I am using the below, which return the above xml
XDocument xdoc = XDocument.Parse(result1);
I have tried about 4000 different things, including:
IEnumerable<XElement> otinStatus = from el in xdoc.Elements("customer") select el;
IEnumerable<XElement> otinStatus2 = from el in xdoc.Elements("cusotmer.optinStatus") select el;
IEnumerable<XElement> otinStatus3 = from el in xdoc.Elements("optinStatus") select el;
All of which returns no results.
Please help, I know this is something simple I am missing.
Thank you in advance -- Joe
Retrieve an Element's Value
var status = xDoc
.Descendants("optinStatus") // get the optinStatus element
.Single() // we're expecting a single result
.Value; // get the XElement's value
Example
Here is a working Fiddle for you. You can see it running live here. The output is 3.
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
public class Program
{
public static void Main()
{
var xDoc = XDocument.Parse(xmlString);
var status = xDoc.Descendants("optinStatus").Single();
Console.WriteLine(status.Value);
}
private static string xmlString = #"
<response op=""getcustomerinfo"" status=""200"" message=""ok"" version=""1.0"">
<customer>
<totalMsg>3</totalMsg>
<custId>9008281</custId>
<custName></custName>
<timestamp>2015-04-30 16:17:19</timestamp>
<optinStatus>3</optinStatus>
<custMobile>6185312349</custMobile>
<subacct>1st Choice Courier</subacct>
</customer>
</response>
";
}
Explanation
Descendents() is an instance axes method (or just axes in shorthand). It returns an IEnumerable<XElement> of all matching descendents. On its results, we call Single(). It is a Linq method that returns the only element of a sequence. If there is more than one element, it throws an error. We're left with a single XElement. This represent an entire XML element. Since we only want its value not the entire element, we call the Value property. Bingo, we're done.
A Bit More Detail
Axes come in two kinds:
Instance axes methods, which MSDN lists here, are invokable members of the XElement, XDocument, and XNode classes.
Extension axes methods, which MSDN lists here, are invokable on collections.
With one exception, an axes method returns a collection of type IEnumerable<T>. The exception is Element(), which returns the first matching child object. That what AmatuerDev used and, as in your question, if you are only expecting a single result, it is a just as good if not better approach that is Descendants().
Retrieve an Attribute Value
Once we have an XElement, we can retrieve one of its attributes instead of its value. We do that by calling the Attributes() method. It returns the matching XAttribute. Since we only want the attribute value, we call the Value property. Voila.
// for attribute
var response = xDoc.Descendants("response").Single();
var attr = response.Attribute("status");
General Approach
Using Linq to XML is a two step process.
Call an axes method to obtain an IEnumerable<T> result.
Use Linq to query that.
See Also
Here is some relevant MSDN documentation:
How to: Retrieve the Value of an Element (LINQ to XML)
LINQ to XML Axes
Assuming xDoc being the XDocument. Have you tried..
var customer = xDoc.Root.Element("customer");
var optinStatus = customer.Element("optinStatus");
var optinStatusValue = optinStatus.Value;

What is the proper way to retrieve a value from a single, nested, XElement?

To retieve the value of a nested XElement I have the following in my code:
XElement xml = new XElement("UserDefinedSettings", new XElement("RootFolder", new XElement("FolderName", "User Input Goes Here")));
xml.Save("UserDefinedSettings.xml");
Which gives me this saved to the hard drive:
<?xml version="1.0" encoding="utf-8"?>
<UserDefinedSettings>
<RootFolder>
<FolderName>User Input Goes Here</FolderName>
</RootFolder>
</UserDefinedSettings>
Later, To retrieve the name of the folder that the user has chosen I am using:
XDocument xdoc = XDocument.Load("UserDefinedSettings.xml");
var myVar = xdoc.Descendants("FolderName").Single();
textBox1.Text = myVar.Value;
I am new to Linq-XML and I am wondering if what I have done is the right way to go about it?
Initially I had been using the following line of code to get the name of the folder, but I knew there had to be a better way and after searching here on SO for examples I am using the code above instead.
string s =xdoc.Element("UserDefinedSettings").Element("RootFolder").Element("FolderName").Value;
What you have should be fine (the newer way), as long as you are certain those elements exist. It runs the risk of throwing a null reference exception however if any of them do not exist. I typically query with Elements() rather than Element(). Elements() returns an IEnumerable which you can safely chain together with more Elements() queries (or whatever). For example, you might consider:
var folder = (string)xdoc.Elements("UserDefinedSettings").Elements("RootFolder").Elements("FolderName").FirstOrDefault();
Another thing I typically do when I want the value from an attribute or element is cast my XElements and XAttributes with a string as I did above. A null value cast as a string will return a null string, preventing a null reference exception that you would get with a .Value call.
Also would work:
xdoc.XPathSelectElement("/UserDefinedSettings/RootFolder/FolderName").Value
I prefer to use XPath for it's succinctness but it's your choice.

Using xPath in C# to get value of node attribute

If I have the following xml document:
<xml>
<data>
<dataset name="X"></dataset>
</data>
</xml>
How can I use Xpath in c# to retrieve the value of the name attribute (i.e. X)
How can I use Xpath in c# to retrieve the value of the name attribute
(i.e. X)
This XPath expression:
/xml/data/dataset/#name
selects the wanted attribute -- all atributes named name that belong to a dataset element that is a child of a data element that is a child of the top element of the XML document.
However, you want to get the value of the attribute -- not the node itself.
This XPath expression:
string(/xml/data/dataset/#name)
when evaluated, produces the wanted string value.
In C# use the XPathNavigator.Evaluate() method to evaluate the expression above.
Use this XPath:
xml/data/dataset/#name
use this XPath expression:
xml/data/dataset
this will retrieve the dataset node. after that you can use C# tools to retrieve the attribute name from the node.

Load in a simple list of objects defined in xml as an object in C#

I am a university student taking a HCI design course, and using C# and WPF for the first time. I have read a little about xml, and it seems like a good way to get input for use in my program. I have an XML file i made that contains a list of houses and there peramaters, like so:
<house>
<Price>400000</price>
<neighborhood>BrentWood</neighborhood>
<description>This is a so and so house, located...</description>
</house>
<house>
<Price>300000</price>
<neighborhood>BrentWood</neighborhood>
<description>This is a so and so house, located...</description>
</house>
And i have a house class like so:
public class house{
public house(int price, string neighborhood, string description){
this.price = price;
this.neighborhood = neighborhood;
this.description = description;
}
public int price;
public string neighborhood;
public string description;
}
I have read a little about xml, but i cant seems to find a tutorial to make a function that takes the xml file as input, and returns a List of newly created house objects. Can anyone show me how this is done? Or maybe suggest a better way of defining the house objects in a file, and loading them as house objects?
This should get you started using LINQ to XML:
XDocument housesXml = XDocument.Load("houses.xml");
List<House> houses =
housesXml.Root.Elements("house")
.Select(h => new House(
int.Parse(h.Element("price").Value),
(string) h.Element("neighborhood"),
(string) h.Element("description")
))
.ToList();
(Also, wrap your <house> elements in an outer <houses></houses> root tag, and take care to match case, <Price></price> should be <price></price>)
Check these link that help you read the XML file in C# and better guide you which way is good to read it fast:
How to read XML from a file by using Visual C#
Using XML in C# in the simplest way
Reading xml fast
First open your file with XmlTextReader class.
XmlTextReader reader = new XmlTextReader ("books.xml");
After you create the XmlTextReader object, use the Read method to read the XML data. The Read method continues to move through the XML file sequentially until it reaches the end of the file, at which point the Read method returns a value of "False."
To process the XML data, each record has a node type that can be determined from the NodeType property.
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element: // The node is an element.
Console.Write("<" + reader.Name);
Console.WriteLine(">");
break;
case XmlNodeType.Text: //Display the text in each element.
Console.WriteLine (reader.Value);
break;
case XmlNodeType. EndElement: //Display the end of the element.
Console.Write("</" + reader.Name);
Console.WriteLine(">");
break;
}
}
On the place of Console.WriteLine fill your house object with reader.name or properties that you have created in the xml file..
Check these for reading XML file from Linq.
LINQ to read XML
Reading XML documents using LINQ to XML
Check this MSDN tutorial .NET Language-Integrated Query for XML Data
Create Properties in your class rather than creating public elements
and constructor for better implementation.
Or you could use the XmlTextReader class
1. Correct your tags. You cannot have opening "Price" and closing "price" they do not match and it will cause errors.
2. You have to have root element in your XML. Your document should start with some element and close with it (root can be houses).
3. You can load objects using Linq 2 XML:
XElement element = XElement.Parse(...) // or XDocument.Load
List<house> myList = (from item in element.Descendants("house")
select new house(Convert.ToInt32(item.Element("price").Value),
item.Element("neighborhood").Value,
item.Element("description").Value)).ToList();

Categories