Add elements to XDocument after LINQ Query - c#

I have the following XML LINQ query from my XDocument.
var totals = (from x in MyDocument.Descendants("TOTALS") select x).FirstOrDefault();
Once I have found my totals node I need to add some elements to that node and push that change to the XDocument.

So just make the change to the returned node... unless you clone it, it will still be part of the document.
Btw, your query expression isn't adding anything - simpler code would be:
var totals = MyDocument.Descendants("TOTALS").FirstOrDefault();

you can use AddAfterSelf() to add new nodes against totals. Those changes will automatically get attached to the main XDocument, since totals is referencing an XElement inside the document.

totals.Add(new XElement("NewNode", "New node value"));

Related

C# get all children of XElement regardless of their value

I have the following XML structure:
<init_deinit>
<step name="init">
<call>...</call>
<check>...</check>
<call>...</call>
<wait>...</wait>
....
</step>
<step name="deinit">
....
</step>
</init_deinit>
There is a lot of examples of how to retrieve all descendants of a single type. I.E.:
XDocument xdoc = XDocument.Load("file.xml")
var all_call_tags = xdoc.Descendants("init_deinit").Elements("step").ElementAt(0).Elements("call");
But I need to retrieve ALL the children of the 'step' element and I need to retrieve them in the exact order thay are written in the XML. So what I need is something like IEnumerable iterator that contains XElements call, check, call and wait in this order. I tried but failed so far :)
Thank you for your advice!
This will give you all Descendants of step elements:
xdoc.Descendants("step").SelectMany(x => x.Descendants());
If you want Descendants of first step element use
xdoc.Descendants("step").First().Descendants();
Please try this :
XDocument xdoc = XDocument.Load("file.xml");
//Here you will get all the descendants of the first step
xdoc.Descendants("step").First().Descendants();
//To get all Descendants of step elements:
var x = xdoc.Descendants("step").Descendants();

Get all nodes and values without knowing the nodes name and level C#

I have about 15,000 XML in the form of string. Each of the XML has an average of 1000 nodes.
I do not know the nodes name, and the hierarchical level of the XML. For each XML, I need to parse them into List<string> elements and List<string> values.
In a case where parent and child nodes are present, the parent node will be added into the List<string> elements and a null or empty string will be added to List<string> values
What are the possible ways of achieving so?
Edited: I supposed I just need to know how to parse one XML, and I can loop the same method for all 15,000 records.
p/s: I thought of using Dictionary or multi-dimensional List where I could have something like <key><value> pair, but it wasn't approved because it will affect other application significantly. So it has to be a List of Elements and a List of Values
You can use LINQ to get all the nodes from XML. You'll need to add using System.Xml.Linq; to your parsing class, then you can grab the data like this.
string xml = "your xml string"
var myXmlData = XElement.Parse(xml);
//Get the names of all nodes
var allNames = (from e in myXmlData.Descendants()
select e.Name.LocalName).ToList();
//Get the values of each node - empty string for nodes with children
var allElements = (from e in myXmlData.Descendants()
select (e.HasElements ? "" : e.Value)).ToList();
This will give you two List<string> objects with all the corresponding names and values for your XML.

Info from xml to c# with irregular xml

I need to get info from an XML-file in C#.
Here is some fragments from the XML.
<Surface id="su-62" surfaceType="InteriorWall">
<Name>S-3-7-I-W-62</Name>
<AdjacentSpaceId spaceIdRef="sp-3-TUIN">
</AdjacentSpaceId>
<AdjacentSpaceId spaceIdRef="sp-7-huizen">
</AdjacentSpaceId>
<CADObjectId>Basic Wall: _omgevingsmuur [184610]</CADObjectId>
</Surface>
...
<Surface id="su-63" surfaceType="ExteriorWall">
<Name>N-4-E-W-63</Name>
<AdjacentSpaceId spaceIdRef="sp-4-onthaal">
</AdjacentSpaceId>
<Opening id="su-63-op-1" openingType="NonSlidingDoor">
</Opening>
<CADObjectId>Basic Wall: _detentiemuur [193431]</CADObjectId>
</Surface>
...
<Surface id="su-282" surfaceType="Shade">
<Name>X-S-282</Name>
<CADObjectId>Basic Roof: Generic - 400mm [190976]</CADObjectId>
</Surface>
As you see there are some surfaces that don't have all the info others have.
I have to know which surfaces are adjacent to which space and if there is an opening or not.
(The ultimate goal is to make an 2d array where you can see which SPACE is adjacent to which SPACE and another array to see if the have a joined opening.)
Assuming you have .NET 3.5 or greater, than this is a good use for LINQ2XML.
You can write a query that will grab the associated areas an identify which space are adjacent to each other.
// Load your XML File into an XDocument object
var xDoc = XDocument.Load(xmlPath);
// this is your query, in the end result with my a Dictionary with the Surface
// Id attribute as the key and the AdjacentSpaceId as the value
var result = (from x in xDoc.Descendants("AdjacentSpaceId")
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
}).GroupBy(sp => sp.SurfaceId)
.ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
If you are unfamiliar with any of the LINQ varients, I will attempt to explain the query in a bit more detail. The local field containing your query results is just called results. The var type is another .NET 3.5 addition that tells the compiler to identify the type by the right side of the expression. In this case, result will be a Dictionary<String, IEnumerable<String>>.
In the first line of the query:
from x in xDoc.Descendants("AdjacentSpaceId")
You are basically telling LINQ to iterate through all nodes in the XML called AdjacentSpaceId. It doesn't matter where these nodes are located or what the parent nodes are called. The Descendants() method in LINQ can be very powerful for this reason as it means you don't need to know the exact XML Path to a particular node to select it, you just need the name (see my note at the end)
The next lines of the query are defining exactly what you want to return from the query:
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
})
All LINQ queries using this syntax must have a select statement (there is also a method syntax that can be used instead of or in addition too that doesn't need a select necessarily, but this syntax is easier to learn in my opinion).
In the select clause, you are basically defining a new Anonymous Type that has 3 properties, SurfaceId, SurfaceName, and AdjacentSpace. I'll explain one of the assignments and it should let you understand all of them:
SurfaceId = (String)x.Parent.Attribute("id")
In this line, x refers to the initial line of the LINQ query where you were iterating all of your AdjacentSpaceId nodes, so x is of type XElement.
The Parent property of x is going to select the parent node, in this case Surface. (NOTE: If your root node in your XML happens to be called AdjacentSpaceId, then this line is going to throw an exception since the parent of a root node will be null, but seems to be a safe assumption that it won't be a problem in this case). The Parent property on XElement is also going to be another XElement.
The next part is the Attribute method of XElement, and you are selecting the first attribute of node called id.
So effectively you are selecting the id attribute of all parent nodes to each AdjacentSpaceId nodes.
And lastly, I am casting this to a String. There is a Value property that can be used instead, but my personal preference is to cast to a String because if the Attribute("id") method fails (which could happen if there is no attribute called "id"), calling the Value property will throw an exception, whereas casting to a string will just return null in these cases.
The other parts of this select clause are virtually the same.
The rest of this query are effectively separate queries. I just chained them together, but they could easily be broken out and put on their own.
The next piece:
GroupBy(sp => sp.SurfaceId)
is grouping the result of your LINQ query by the SurfaceId property. This is a key step because it sounds like you want to know where spaces are adjacent to each surface and this will effectively group all of the adjacent spaces together by the surface.
If you aren't familiar, sp => sp.SurfaceId is a Lambda Expression for creating anonymous functions or delegates quickly and easily.
And the final piece will take your grouped results and convert them into something more usable, usually a Dictionary<>, or in this case a Dictionary<String, IEnumerable<String>>
ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
Just one side note about the Descendants() extension method for Linq-to-Xml objects, while it can be very useful as I mentioned above, it can also be very dangerous. When you have multiple nodes in the same XML with the same name but with different purposes or in different parents of the tree, Descendants() will return ALL objects. If you only want to return some of the nodes matching a specific name, you need to filter it down first by using the Element() or Elements() extension methods first to select the proper parent node, then you can safely call Descendants().

Check all the children for XElement

I have XElement object which is my XML tree read from XML file. Now I want to check all the nodes in this tree to get first attribute name and value. Is there any simple way to go through all of the nodes (from root till leaves)? My XML file has got very many different and strange nodes - that's why it's harder to solve this issue. I thought about writing some recursion, but hope it's another way to solve that easier.
Maybe take a look to Xpath. an XPath like this //*[#id=42] could do the job.
It means get all nodes which have an attribute "id" of value 42.
You can do just //* which gonna returns all nodes in a tree.
Xpath :
http://msdn.microsoft.com/en-gb/library/ms950786.aspx
Syntax :
http://msdn.microsoft.com/en-us/library/ms256471.aspx
You can get all children elements using XElement.Elements().
Here's some code using recursion to get all elements of each level:
void GetElements(XElement element){
var elements = element.Elements();
foreach(Element e in elements){
//some stuff here
if(e.Elements() != null)
GetElements(e);
}
}

Select child nodes, but ignore non-elements with XPath?

Given the following XML document for example:
<?xml version="1.0"?>
<UrdaObject>
<Date>
<Year>2011</Year>
<Month>5</Month>
<Day>18</Day>
<Hours>8</Hours>
<Minutes>47</Minutes>
<Seconds>36</Seconds>
</Date>
<random_value>24</random_value>
</UrdaObject>
And the understanding the child::node() - Selects all child nodes of the current node how would I create an XPath (starting from the root) that would select all child nodes EXCEPT text, comments, and other things that are NOT elements. For example, when using this code to create a tree view in WPF:
// x is some XmlDocument, xmlTree is my WPF TreeView
XmlDataProvider provider = new XmlDataProvider();
provider.Document = x;
Binding binding = new Binding();
binding.Source = provider;
binding.XPath = "child::node()";
xmlTree.SetBinding(TreeView.ItemsSourceProperty, binding);
How would I go about creating my XPath statement so I build a treeview with nodes going all the way down and stopping before the raw text? For example it would generate a view of:
UrdaObject
Date
Year
...
Instead of...
UrdaObject
Date
Year
2011 (Don't want this!)
...
The sample XML files is just for me to explain my situation. The expression should be able to navigate any valid XML file and pull the elements, but not the individual text.
How did we fix this? I had switched all references of child::node() to child::*. However, I had NOT corrected one line in my XAML, which was pulling child::node(). Correcting this line made the application behave correctly... and made me feel silly.
child::node() finds all child nodes. child::* finds all element nodes.
it's as simple as *.
(that gets immediate children, however; if you want all descendant elements, it would be descendant::*)
child::* will exclude text nodes and leave only element nodes
child::text() will include only text nodes
child::node() will include both element and text nodes
http://www.w3.org/TR/xpath/#location-paths
Not sure if this is what you want but could it be done this way?
var doc =XDocument.Parse(#"
<UrdaObject>
<Date>
<Year>2011</Year>
<Month>5</Month>
<Day>18</Day>
<Hours>8</Hours>
<Minutes>47</Minutes>
<Seconds>36</Seconds>
</Date>
<random_value>24</random_value>
</UrdaObject>
");
var query = from s in doc.Descendants()
select s.Name;
foreach (var name in query)
{
Console.WriteLine(name);
}

Categories