XElement.Element("Leve1/Level2") instead of XElement.Element("Leve1").Element("Level2") - c#

If I have the following XML in XElement myXML variable in C#,
<Example>
<Level1>
<Level2>myvalue</Level2>
</Level1>
</Example>
To get "myvalue" I need to do as below:
myXML.Element("Leve1").Element("Level2").Value;
Is there any shortcut to do it like:
myXML.Element("Leve1/Level2").Value
Thanks...

You can use xpath with exactly the same syntax you are after:
var myValue = myXML.XPathSelectElement("Level1/Level2").Value;
XPathSelectElement is extension method, so you need to add using System.Xml.XPath; to be able to use it.

Try below code snippet,
//Load xml
XDocument xdoc = XDocument.Parse("<Example><Level1><Level2>myvalue</Level2</Level1></Example>");
//Run query
var lv2s = (from lv2 in xdoc.Descendants("Level2") select lv2.Value).ToList();

I would personally recommend you to use Descendants. If you want to apply certain path using Level1, you could filter by Parent on Level1;
var elementValue = doc.Descendants("Level2")
.Where(x => x.Parent.Name == "Level1")
.Select(x => x.Value).FirstOrDefault();

Related

Linq statement in C# to extract data from XElement

I have a List containing elements like this:
{<d:element m:type="SP.KeyValue" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
<d:Key>Path</d:Key>
<d:Value>https://my.home.site.com</d:Value>
<d:ValueType>Edm.String</d:ValueType>
</d:element>}
I'd like help to discern the Linq statement required to extract only the "https://my.home.site.com" values from said List<>. The catch here is that we cannot only use the <d:Value> because only XElements in this list that has a <d:Key> value of Path, like in the example above, actually contain URLs in the <d:Value> key.
Does anyone know the magic Linq statement that would perform said data extract?
Assuming your data is coming from an XML file similar to this:
<?xml version="1.0"?>
<root xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
<d:element m:type="SP.KeyValue">
<d:Key>Path</d:Key>
<d:Value>https://my.home.site.com</d:Value>
<d:ValueType>Edm.String</d:ValueType>
</d:element>
<d:element m:type="SP.KeyValue">
<d:Key>NotPath</d:Key>
<d:Value>https://my.home.site.com</d:Value>
<d:ValueType>Edm.String</d:ValueType>
</d:element>
</root>
The following code:
XElement root = XElement.Load("Some file");
List<string> urls;
//Query Syntax
urls = (from e in root.Elements(d + "element")
where e.Element(d + "Key").Value == "Path"
select e.Element(d + "Value").Value);
//Or
//Method Syntax
urls = (from e in root.Elements(d + "element")
where e.Element(d + "Key").Value == "Path"
select e.Element(d + "Value").Value).ToList();
Console.WriteLine(string.Join(",", urls));
Will result in (note that it ignores the "NotPath" key):
https://my.home.site.com
You can check out a live example here and check out this for more XElement information.
if you actually have a List of XElement:
var list = new List<XElement>(); //however you get your XElement collection
var values = list.Where(x => x.Elements().First(e => e.Name.LocalName == "Key").Value == "Path")
.Select(x => x.Elements().First(e => e.Name.LocalName == "Value").Value)
if you have an XDocument, you'd just modify the beginning of the query slightly.
I think that problem if with naespace declaration. Try this:
string xml = "<d:element m:type=\"SP.KeyValue\" xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\" xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\">"+
"<d:Key>Path</d:Key>"+
"<d:Value>https://my.home.site.com</d:Value>"+
"<d:ValueType>Edm.String</d:ValueType>"+
"</d:element>";
XDocument xmlObj = XDocument.Parse(xml);
XNamespace ns_d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
var result = xmlObj.Descendants(ns_d + "Value").Select(x => x.Value);

How to find and change inner text of xml elements? C#

I have a following xml file. I need to change the inner text of ANY tag, which contains the value «Museum», or just a tag for a start:
<src>
<riga>
<com>¾</com>
<riquadro797>Direction to move</riquadro797>
</riga>
<riga>
<llt>
<com>Museum</com>
<elemento797>Direction not to move</elemento797>
</llt>
</riga>
<operation>
<com> </com>
<riquadro797>Museum</riquadro797>
</operation>
<riga>
<gt>
<elemento797>Direction not to move</elemento797>
</gt>
</riga>
</src>
I've parsed this file to XElement. What I've tried and it dos not work:
var tt = xmlCluster.Elements(First(x => x.Value == "Museum");
This code is not proper, as I cannot predict which element will contain "Museum":
var el = rootElRecDocXml.SelectSingleNode("src/riga/gt/elemento797[text()='"+mFilePath+"']");
How to do it? Any help will be greatly appreciated!
just grab all elements with Museum values:
var doc = XDocument.Parse(xml);
var elements = doc.Descendants().Where(e => e.Value == "Museum");
foreach (var ele in elements)
ele.Value = "Test";
//doc is updated with new values
as Selman22 noted, doc will just be a working copy of your xml. You'll need to call doc.Save to apply anything back to the disk, or wherever you need
Elements() only looks at a single level in the heirarchy. I think you want Descendants() instead...
If you want an older-school XPath option, you need to do a global search on the tree - you can use the // XPath expression for this:
var els = rootElRecDocXml.SelectNodes("//[text()='"+mFilePath+"']");

Use LINQ XML with a namespace

I am trying to find nodes in an XML document like this:
<?xml version="1.0"?>
<TrainingCenterDatabase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">
<Activities>
<Activity Sport="CyclingTransport">
<Id>2014-07-08T15:28:14Z</Id>
</Activity>
</Activities>
</TrainingCenterDatabase>
I aim to extract the node value 'Id' with code like this:
XDocument doc = XDocument.Load(filePath);
List<string> urlList = doc.Root.Descendants("Id")
.Select(x => (string)x)
.ToList();
Console.WriteLine(urlList.Count);
However the count is 0, where I expect 1.
After some debugging and editing the XML I noticed that if I change the TrainingCenterDatabase node and remove the attributes to this:
<TrainingCenterDatabase>
Then the result is a count of 1 as expected.
So my question is how do I take into account the namespaces so that I can get the value when the TrainingCenterDatabase node has these attributes?
Namespaces in XML can be tricky. I've run into this problem myself a number of times. In all likelihood, the following will fix your problem:
XDocument doc = XDocument.Load(filePath);
List<string> urlList = doc.Root.Descendants(doc.Root.Name.Namespace.GetName("Id"))
.Select(x => (string)x)
.ToList();
Console.WriteLine(urlList.Count);
Basically, this just assumes the underlying element to have the same namespace as your root element. That's true in this case, but of course it doesn't have to be.
The right way, probably, is to do it explicitly. Now, granted, that kind of depends on how you're using this and your datasource, so make the decision for yourself, but that would require doing something more like this:
XDocument doc = XDocument.Load(filePath);
List<string> urlList = doc.Root.Descendants(System.Xml.Linq.XName.Get("Id", "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"))
.Select(x => (string)x)
.ToList();
Console.WriteLine(urlList.Count);
The cause for your problem was that the default behavior for XElement, when not given an explicit namespace, is to assume no namespace. However, the default behavior for the XML spec is to assume the parent's namespace. In your case, those two were different, so it wasn't able to find the descendant.
It Works...
XDocument doc = XDocument.Load(filePath);
XNamespace ns = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2";
var root = doc.Descendants(ns + "Id").Select(x => x.Value).ToList();
Console.WriteLine(root.Count);

Best way to query XDocument with LINQ?

I have an XML document that contains a series of item nodes that look like this:
<data>
<item>
<label>XYZ</label>
<description>lorem ipsum</description>
<parameter type="id">123</parameter>
<parameter type="name">Adam Savage</parameter>
<parameter type="zip">90210</parameter>
</item>
</data>
and I want to LINQ it into an anonymous type like this:
var mydata =
(from root in document.Root.Elements("item")
select new {
label = (string)root.Element("label"),
description = (string)root.Element("description"),
id = ...,
name = ...,
zip = ...
});
What's the best way to pull each parameter type according to the value of its 'type' attribute? Since there are many parameter elements you wind up with root.Elements("parameter") which is a collection. The best way I can think to do it is like this by method below but I feel like there must be a better way?
(from c in root.Descendants("parameter") where (string)c.Attribute("type") == "id"
select c.Value).SingleOrDefault()
I would use the built-in query methods in LINQ to XML instead of XPath. Your query looks fine to me, except that:
If there are multiple items, you'd need to find the descendants of that instead; or just use Element if you're looking for direct descendants of the item
You may want to pull all the values at once and convert them into a dictionary
If you're using different data types for the contents, you might want to cast the element instead of using .Value
You may want to create a method to return the matching XElement for a given type, instead of having several queries.
Personally I don't think I'd even use a query expression for this. For example:
static XElement FindParameter(XElement element, string type)
{
return element.Elements("parameter")
.SingleOrDefault(p => (string) p.Attribute("type") == type);
}
Then:
var mydata = from item in document.Root.Elements("item")
select new {
Label = (string) item.Element("label"),
Description = (string) item.Element("description"),
Id = (int) FindParameter(item, "id"),
Name = (string) FindParameter(item, "name"),
Zip = (string) FindParameter(item, "zip")
};
I suspect you'll find that's neater than any alternative using XPath, assuming I've understood what you're trying to do.
use XPATH - it is very fast ( except xmlreader - but a lot of if's)
using (var stream = new StringReader(xml))
{
XDocument xmlFile = XDocument.Load(stream);
var query = (IEnumerable)xmlFile.XPathEvaluate("/data/item/parameter[#type='id']");
foreach (var x in query.Cast<XElement>())
{
Console.WriteLine( x.Value );
}
}

remove sections of XML document with Linq

How would i using Linq remove all section where their element contains parameter with {} ? In my example i want to remove section with {SecName1}
Source document:
<ReceiptLayoutMaintenanceRequest>
<ReceiptLayoutName>Test Layout1</ReceiptLayoutName>
<ActionName>Add</ActionName>
<ReceiptLayoutForMaintenance>
<Name>Test Layout1</Name>
<Description>ReciptDesc</Description>
<PrinterName>Emulator - Receipt</PrinterName>
<ReceiptLayout>
<Name>AAA</Name>
<Description>$</Description>
<TemplateName>DefaultTemplate</TemplateName>
<LayoutParameters />
</ReceiptLayout>
<ReceiptLayout>
<Name>{SecName1}</Name>
<Description>$</Description>
<TemplateName>DefaultTemplate</TemplateName>
<LayoutParameters />
</ReceiptLayout>
</ReceiptLayoutForMaintenance>
</ReceiptLayoutMaintenanceRequest>
Wanted output
<ReceiptLayoutMaintenanceRequest>
<ReceiptLayoutName>Test Layout1</ReceiptLayoutName>
<ActionName>Add</ActionName>
<ReceiptLayoutForMaintenance>
<Name>AAA</Name>
<Description>ReciptDesc</Description>
<PrinterName>Emulator - Receipt</PrinterName>
<ReceiptLayout>
<Name>AAA</Name>
<Description>$</Description>
<TemplateName>DefaultTemplate</TemplateName>
<LayoutParameters />
</ReceiptLayout>
</ReceiptLayoutForMaintenance>
thanks
This removes any ReceiptLayout node which has a child Name that starts and ends with brackets and produces your desired output:
XDocument doc = XDocument.Load(#"test.xml"); //load xml
var nodesToRemove = doc.Descendants("ReceiptLayout")
.Where(x => x.Element("Name").Value.StartsWith("{")
&& x.Element("Name").Value.EndsWith("}"))
.ToList();
foreach (var node in nodesToRemove)
node.Remove();
This can be shortened into one Linq statement, personally I prefer to keep Linq query and modification (removal) separate though:
doc.Descendants("ReceiptLayout")
.Where(x => x.Element("Name").Value.StartsWith("{")
&& x.Element("Name").Value.EndsWith("}"))
.Remove();
var doc = XDocument.Parse(xml);
doc.Descendants()
.Where(n => !n.HasElements && Regex.IsMatch(n.Value, "[{].*?[}]"))
.Select(n=>n.Parent) // because you want to remove the section not the node
.Remove();
xml = doc.ToString();
A variant of BrokenGlass code using the let keyword
var doc = XDocument.Load(#"test.xml");
var list = from p in doc.Descendants("ReceiptLayout")
let q = p.Element("Name")
let r = q != null ? q.Value : string.Empty
where r.StartsWith("{") && r.EndsWith("}")
select p;
list.Remove();
This is "premature optimization" :-) I "cache" the p.Element("Name").Value. Ah... And I check if really there is a Name Element so that everything doesn't crash if there isn't one :-)

Categories

Resources