When parsing XML with Linq, only one object gets fetched - c#

I'm trying to populate an array with the following xml:
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<data>
<item>
<date>1307018090</date>
<price>10.4718867</price>
<low>10.38100000</low>
<high>10.49000000</high>
<nicedate>14:39</nicedate>
</item>
<item>
...
</item>
and so on
I'm using this Linq-query, which to me means that It'll create one object per :
var items = from item in doc.Element("data").Descendants()
select new Currency
{
Close = item.Element("price").Value.ToString(),
Date = item.Element("date").Value.ToString(),
Low = item.Element("low").Value.ToString(),
High = item.Element("high").Value.ToString(),
Time = item.Element("nicedate").Value.ToString()
};
And when I foreach through items, only one item gets selected. I'm not very used to Linq so I can't figure out how to properly construct this statement. Any suggestions?

You need to start the Linq-Xml like so
var items =
from item in
doc.Element("data")
.Elements("item")

Descedants() method returns not only children, but also grand-children, grand-grand-children etc. So, the second tag that gets processed by LINQ is your first <item>'s <date> and it isn't processed (I think there should be an exception here, can't check at the moment).
Replace your Descedants() call to Elements("item"), as suggested by #DaveShaw

Related

Can't loop children of returned XML Nodes in C#

I have a large, messy XML file and I want to retrieve ALL elements of the same name ("Item" for the sake of this post) from it, then be able to retrieve data from each element's children.
So far I have returned a list of every element called "Item" using this code, which just displays the namespace url and "Item" in p tags:
XDocument doc = XDocument.Load(#"C:\inetpub\wwwroot\mysite\myxml.xml");
XNamespace ns = "http://www.mynamespace.com";
var nodes = doc.Descendants().Elements(ns + "Item").Select(d => d.Name).ToList();
foreach(var x in nodes){
<p>#x</p>
}
However, by amending the code with the following, I can't retrieve any data of it's children and I get the error 'System.Xml.Linq.XName' does not contain a definition for 'Descendants':
foreach(var x in nodes){
<p>#x.Descendants().Element("Name")</p>
}
Here is a very basic version of my XML file:
<Item>
<Name>Item 1</Name>
<Type>Type 1</Type>
</Item>
I want to be able to search each 'Item' element for a 'Name' element and return the value. Can anyone see where I'm going wrong?
This is the problem:
.Select(d => d.Name)
You're explicitly selecting the names of the elements. If you want the actual elements (which I think you do), just get rid of that call:
var nodes = doc.Descendants().Elements(ns + "Item").ToList();
You could also get rid of the ToList() unless you need the query to be materialized eagerly.

Auto increment ID value in xml file

I want to add a new node to my xml file, but also, i would like to add id value in it, but incremented by 1 from the last value. Here is my XML:
<users>
<user>
<id>1</id>
<name>Name1</name>
<surname>Surname1</surname>
<weight>78</weight>
<height>180</height>
</user>
<user>
<id>2</id>
<name>Name2</name>
<surname>Surname2</surname>
<weight>84</weight>
<height>180</height>
</user>
</users>
And here is my code so far (for adding a new node):
XmlNode node = xmlDoc.SelectSingleNode("/users/user");
XmlNode newNode = xmlDoc.ImportNode(node, true);
xmlDoc.DocumentElement.AppendChild(newNode);
xmlDoc.SelectSingleNode("users/user/id").InnerText = ; // <-- ??
xmlDoc.SelectSingleNode("users/user/name").InnerText = nameBox.Text;
xmlDoc.SelectSingleNode("users/user/surname").InnerText = surnameBox.Text;
xmlDoc.SelectSingleNode("users/user/weight").InnerText = Convert.ToString(weightUpDown.Value);
xmlDoc.SelectSingleNode("users/user/height").InnerText = Convert.ToString(heightUpDown.Value);
I am using winforms in C#, in this case get the value from the text boxes and UpDown lists.
How can I do it in c#?
You can use LINQ to XML for that.First get the current element count, then insert a new element like this:
var xDoc = XDocument.Load("path");
var count = xDoc.Descendants("user").Count();
var newUser = new XElement("user",
new XElement("id", count+1),
new XElement("name", nameBox.Text),
new XElement("surname", surnameBox.Text),
new XElement("weight", weightUpDown.Value),
new XElement("height", heightUpDown.Value));
xDoc.Root.Add(newUser);
xDoc.Save(path);
I would suggest you to get element count first (maybe in Form_load) then store it into a variable.By doing that you don't need to perform this query each time you want to add new item.You just need to increment the count.
You can get the max id as follows:
var maxId = xmlDoc.SelectNodes("/users/user/id")
.Cast<XmlNode>()
.Max(node => int.Parse(node.InnerText));
using the count to determine the Id may not work correctly if nodes are removed from the XML, during execution of the program.
Use Guid.NewGuid() instead to create a unique ID
I think I need some more information...
At first glance it looks like you will need to do the following:
Iterate / loop through the values in text boxes and/or UpDown Lists.
Make sure your loop has a counter variable that is created outside of the loop "int counter = 1".
For each item you are iterating through use the counter to set the id in the xml you are generating.
Before the item goes back to looping you should then increment the counter + 1.
You will be auto-populating each node with the values anyways correct?
All you will need is to add this counter variable and you should have basically what you need.

How to get value of child node from XDocument

I need to get value of child node from XDocument using linq
<root>
<Cust>
<ACTNumber>1234</ACTNumber>
<Address>
<Street></Street>
<City>123 Main street</City>
<State>AL</State>
</Address>
</Cust>
</root>
I tried this: xDocTest.Root.Elements("Cust").Elements("ACTNumber")
If I try Address instead of ACTNumber then it works. But its not giving the child node value.
If there's only one Cust element and only one ACTNumber element, then it's easy:
string actNumber = doc.Root.Element("Cust").Element("ACTNumber").Value;
Or to get it as a long:
long actNumber = (long) doc.Root.Element("Cust").Element("ACTNumber");
Use this:
xDocTest.Root.Element("Cust").Element("Adress").Element("City").Value
If you use Elements (note the plural) it gives u an IEnumerable, this would be used like this:
XML
<Father>
<Child>Hello</Child>
<Child>World!</Child>
</Father>
C#
foreach(var childElement in Root.Elements("Child")) Console.WriteLine(childElement.Value);
Or to take your example:
foreach(var child in xdoc.Root.Element("Cust").Element("Address").Elements())
Console.WriteLine(string.Format("{0} : {1}", child.Name, child.Value);
Im not sure how Element behaves if you have multiple Elements of the same name. So you might want to use Elements and Inerate over all occurences.
And in Linq
If there is more than one Customer...
var result = from cust in xdoc.Root.Elements("Cust")
where cust.Elements("ACTNumber").Any() // This is to make sure there
// is an element called ACTNumber
// otherwise .Value would create
// Nullrefexception.
select child.Element("ACTNumber").Value;

JsonConvert.DeserializeXmlNode - must begin with an object

I'm getting a JsonSerializationException calling DeserializeXmlNode() on JSON data that starts with [[ (i.e. it's an array of arrays).
What is the best
way to turn this into XML?
Are there any other JSON schemas that can't be turned into XML?
Update: How the XML should appear is an interesting question. Having an array of arrays means there is no root node (that's an easy one - insert ) but also the set of children nodes have no name. I'm not sure what makes sense here. And this may be a deal killer for using XPath on JSON. So on this part too, any suggestions?
Update 2 - the JSON data:
[["P0010001","NAME","state"],
["4779736","Alabama","01"],
["710231","Alaska","02"],
["6392017","Arizona","04"],
["2915918","Arkansas","05"],
["37253956","California","06"],
["5029196","Colorado","08"],
["3574097","Connecticut","09"],
["897934","Delaware","10"],
["601723","District of Columbia","11"],
["18801310","Florida","12"],
["9687653","Georgia","13"],
["1360301","Hawaii","15"],
["1567582","Idaho","16"],
["12830632","Illinois","17"],
["6483802","Indiana","18"],
["3046355","Iowa","19"],
["2853118","Kansas","20"],
["4339367","Kentucky","21"],
["4533372","Louisiana","22"],
["1328361","Maine","23"],
["5773552","Maryland","24"],
["6547629","Massachusetts","25"],
["9883640","Michigan","26"],
["5303925","Minnesota","27"],
["2967297","Mississippi","28"],
["5988927","Missouri","29"],
["989415","Montana","30"],
["1826341","Nebraska","31"],
["2700551","Nevada","32"],
["1316470","New Hampshire","33"],
["8791894","New Jersey","34"],
["2059179","New Mexico","35"],
["19378102","New York","36"],
["9535483","North Carolina","37"],
["672591","North Dakota","38"],
["11536504","Ohio","39"],
["3751351","Oklahoma","40"],
["3831074","Oregon","41"],
["12702379","Pennsylvania","42"],
["1052567","Rhode Island","44"],
["4625364","South Carolina","45"],
["814180","South Dakota","46"],
["6346105","Tennessee","47"],
["25145561","Texas","48"],
["2763885","Utah","49"],
["625741","Vermont","50"],
["8001024","Virginia","51"],
["6724540","Washington","53"],
["1852994","West Virginia","54"],
["5686986","Wisconsin","55"],
["563626","Wyoming","56"],
["3725789","Puerto Rico","72"]]
I had an array of objects, shaped like:
[{foo:bar}, {foo:bar2}]
...What I did to work around this problem is to wrap the text first like so:
public XmlDocument JsonArrayToXml(string json)
{
var wrappedDocument = string.Format("{{ item: {0} }}", json);
var xDocument = JsonConvert.DeserializeXmlNode(wrappedDocument, "collection");
return xDocument;
}
This does not throw an error. The shape of the XML resembles:
<?xml version="1.0" encoding="UTF-8"?>
<collection>
<item>
<foo>bar</foo>
</item>
<item>
<foo>bar2</foo>
</item>
</collection>

Linq to XML, extracting attributes and elements

I am new to XML and Linq to XML and I just can't find a good guide that explains how to work with it. I have a simple XML string structured as follows
<mainitem>
<items>
<itemdescription>ABC</itemdescription>
<item>
<itemtext>XXX</itemtext>
</item>
<item>
<itemtext>YYY</itemtext>
</item>
<item>
<itemtext>ZZZ</itemtext>
</item>
</items>
<overalldescription>ABCDEFG</overalldescription>
<itemnodes>
<node caption="XXX" image="XXX"></node>
<node caption="YYY" image="YYY"></node>
<node caption="ZZZ" image="ZZZ"></node>
</itemnodes>
</mainitem>
I am using C# code like
var Items = (from xElem in XMLCODEABOVE.Descendants("item")
select new ItemObject
{
ItemObjectStringProperty = xElem.Element("itemtext").Value,
}
);
to extract a list of the itemtext objects for use with my code. Where I need help is in extracting a list of the caption and image attributes of my node elements. I also need the overalldescription and the itemdescription. I have tried every variation of the above code substituting Descendant for Elements, Element for Attribute etc. I know this is probably a basic question but there doesn't seem to be a straight forward guide out there to explain this to a beginner.
To get the captions
// IEnumerable<string>
var captions = from node in doc.Descendants("node")
select node.Attribute("caption").Value;
Or both the captions and image attributes in one shot:
// IEnumerable of the anonymous type
var captions = from node in doc.Descendants("node")
select new {
caption = node.Attribute("caption").Value,
image = node.Attribute("image").Value
};
For the descriptions:
// null ref risk if element doesn't exist
var itemDesc = doc.Descendants("itemdescription").FirstOrDefault().Value;
var overallDesc = doc.Descendants("overalldescription ").FirstOrDefault().Value;

Categories

Resources