Selecting attribute in xml - c#

I am trying to select "Example language" in the xml code below.
This is the C#:
XNamespace gml = "http://www.example.net/gdl";
XElement Xmlwater = XElement.Parse(e.Result);
listBox3.ItemsSource = from Zwemwater in Xmlwater.Descendants(zwr + "Location")
select new water
{
water_name = water.Element(zwr + "Name").Value,
water_language = water.Element(zwr + "language").Value, // How to select the "nl" ?
};
This is the XML:
<zwr:Location>
<zwr:Name>test<zwr:Name>
<zwr:Example language="nl"> Example text </zwr:beschrijving>
<zwr:Example language="en"/>
</zwr:Location>
What is the best way to get the Example language="nl so that i can bind it.
Thank you in advance.

Assuming water is <zwr:Location> element, you can do as follow :
water_language = water.Element(zwr + "Example").Attribute(zwr + "language").Value
That will select child element of Location that has name Example, then get value of that child element's language attribute. PS: I'm not sure if attribute name considering namespace or not, but in the sample above I assume it is

Try the .Attributes property instead of the .Element property.
water_language = water.Attributes(zwr + "language").Value

Related

Accessing a sub attribute in a XML file using XElement

In C#, I am trying to change an option of a feature in an XML file which presents a Print Ticket and loaded as an XElement by the following code:
XElement ticketRootXElement = null;
using (Stream ticketReadStream = displayedPrintTicket.GetReadStream())
{
ticketRootXElement = XElement.Load(ticketReadStream);
}
The partial XML is something like following:
<?xml version="1.0"?>
<psf:Feature xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework" name="psk:PageMediaSize">
<psf:Option name="psk:ISOA4">
<psf:ScoredProperty name="psk:MediaSizeWidth">
<psf:Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:integer">210000</psf:Value>
</psf:ScoredProperty>
<psf:ScoredProperty name="psk:MediaSizeHeight">
<psf:Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:integer">297000</psf:Value>
</psf:ScoredProperty>
</psf:Option>
</psf:Feature>
How can I access the "Option" of a specific "Feature" and change it to something like <psf:Option name="psk:ISOA3">?
I tried the following code, but it fails.
foreach (XAttribute xAttr in ticketRootXElement.Descendants(xns_psf + "Feature").Attributes())
{
if (xAttr.Value.Equals("psk:PageMediaSize"))
{
foreach(XAttribute xSubAttr in ticketRootXElement.Element("PageMediaSize").Descendants(xns_psf + "Option").Attributes())
{
if (xAttr.NextAttribute.Name.LocalName.Equals("name"))
{
xAttr.NextAttribute.SetValue("psk:ISO" + cmb_PaperSize.SelectedValue.ToString());
}
}
}
}
You can can modify the option value of your selected feature as as follows:
var featureName = "psk:PageMediaSize";
var newOptionValue = "psk:ISOA3"; // Your modified value here
XNamespace xns_psf = #"http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework";
var query = from f in ticketRootXElement.DescendantsAndSelf(xns_psf + "Feature")
where (string)f.Attribute("name") == featureName
select f;
foreach (var f in query)
{
// TODO: handle the situation were a child <psf:Option> element is missing.
f.Element(xns_psf + "Option").SetAttributeValue("name", newOptionValue);
}
Notes:
XElement.Attribute(XName) can be used to look up an attribute by name, and XElement.SetAttributeValue(XName, Object) can be used to set or add an attribute value by name.
Casting an XAttribute to a string returns the value of the attribute, or null if the attribute was missing, and so is convenient to use when filtering by attribute value in a where statement.
If the selected <psf:Feature> element does not have a child <psf:Option> element, the above code will throw an exception. You will need to check your XML schema to determine whether this is possible, and if so, how to handle it.
Demo fiddle here.
Actually I did it using the following code. But "bdc" solution (above answer) sounds much better:
var element = ticketRootXElement.Descendants(xns_psf + "Feature")
.Where(arg => arg.Attribute("name").Value == "psk:PageMediaSize")
.Single();
var subelement = element.Descendants(xns_psf + "Option")
.Single();
subelement.FirstAttribute.SetValue("psk:ISOA3");

Linq to XML Descendants can't read part of xml

I have XML document like this
<document>
<indexroot>
<type>type</type>
<model>model</model>
</indexroot>
<root>
<model_type1>model type 1</model_type1>
<model_type2>model type 2</model_type2>
</root>
</document>
And a linq to xml code:
var elements = (from element in pdb.Descendants()
select new
{
type = (string)element.Element("type") ?? "-",
model= (string)element.Element("model") ?? "-",
model_type1= (string)element.Element("model_type1") ?? "-",
model_type2= (string)element.Element("model_type2") ?? "-"
}).FirstOrDefault();
I get type and a model variables, but it seems I can't reach model_type1 and model_type2, now I understand that this happens because indexroot and root tags, amd if I seperate those tags into diffrent linq to xml code blocks with Descendants("indexroot") and Descendants("root"), everything works fine, but I wan't them in one block, is it possible to achieve that, and how?
You need to navigate down the XML heirarchy for each element you are trying to extract.
This is because the Element method only looks at direct children, not all descendants of a node. From the documentation:
Gets the first (in document order) child element with the specified XName.
One implementation using just Linq-to-XML might be:
xml = "<document>" +
"<indexroot>" +
" <type>type</type>" +
" <model>model</model>" +
"</indexroot>" +
"<root>" +
" <model_type1>model type 1</model_type1>" +
" <model_type2>model type 2</model_type2>" +
"</root>" +
"</document>";
XDocument doc = XDocument.Parse(xml);
var newItem = (from element in doc.Descendants("document")
select new
{
Type = (string)element.Element("indexroot").Element("type") ?? "-",
Model = (string)element.Element("indexroot").Element("model") ?? "-",
ModelType1 = (string)element.Element("root").Element("model_type1") ?? "-",
ModelType2 = (string)element.Element("root").Element("model_type2") ?? "-",
}).FirstOrDefault();
Console.WriteLine(newItem);

How to get an specific node in xml with namespaces?

I'm dealing to access an specific node from a XML Document. I realized that this one as a base namespace. Here is the example.
I'm interested to get the value of the node d:MediaUrl from all descendents node (entry). And I haven't accomplished that.
When I debug the variable iterator 'i', I can see that the XML includes the default namespace again, something like:
<entry xmlns="http://schemas.microsoft.com.ado/..."
And also I have to include the another namespace called 'd'.
What can I do to access to that particular nodes?
This is what I have.
var doc = XDocument.Parse(result);
string BASE_NS = "http://www.w3.org/2005/Atom";
string d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
var query = from i in doc.Descendants(XName.Get("entry", BASE_NS))
select new Image()
{
Url = i.Element(XName.Get("MediaUrl", BASE_NS)).Value
};
var results = query.ToList();
I would suggest using XNamespace rather than XName (personal preference, mainly - as that's how I've always dealt with namespaces in LINQ to XML). To me it's less effort to set up the namespaces in advance and then use Element(NS + "element name") than to useXName.Get(though usingXName.Get` is perfectly fine if that's what you want to do.
If you want to get a all the "MediaUrl" elements for each entry, then I'd do something like this:
XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
var query = (from i in doc.Descendants(d + "MediaUrl")
select new Image()
{
Url = i.Value
}).ToList();
If you want to get only one of them, then you need to do something a little different, depending on which one you wanted to get.
For the properties MediaUrl:
XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
var query = (from i in doc.Descendants(m + "properties")
select new Image()
{
Url = i.Element(d + "MediaUrl").Value
}).ToList();
For the Thumbnail MediaUrl:
XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
var query = (from i in doc.Descendants(d + "Thumbnail")
select new Image()
{
Url = i.Element(d + "MediaUrl").Value
}).ToList();
The key here is to use the namespace in conjunction with the element name in order to retrieve it.
var query = from i in doc.Descendants("{full namespace for prefix d}MediaUrl")
select new Image()
{
Url = i.Value
};

XElement.Element returning null for newly created element

I am using XElement to create an XMLDocument which is used in a hierarchical WPF treeview. If I create a new element with :
x_element = new XElement("node",
new XElement("tree_id", strData[0]),
new XElement("sys_id", dpl.DepSysId),
new XElement("part_id", strData[8]),
new XElement("make", strData[6]),
new XElement("model", strData[5]),
new XElement("level", strData[2]));
I then need to add attributes to "node" so I tried:
XElement temp_el = x_element.Element("node"); // This is returning null
temp_el.SetAttributeValue("title", strData[7] + " " + strData[6] + " " + strData[5]);
temp_el.SetAttributeValue("canEdit", "False");
temp_el.SetAttributeValue("status", nStatus.ToString());
temp_el.SetAttributeValue("qty", strData[13]);
temp_el.SetAttributeValue("part", strData[8]);
In the above code temp_el is null, but I can see in the debugger that x_element contains the following :
<node>
<tree_id>82</tree_id>
<sys_id>82</sys_id>
<part_id>169</part_id>
<make>ST Panel</make>
<model>Logical Pure 16 tube Collector</model>
<level>0</level>
</node>
To work around this I have used the following:
foreach (XElement temp_el in x_element.DescendantsAndSelf())
{
if (temp_el.Name == "node")
{
temp_el.SetAttributeValue("title", strData[7] + " " + strData[6] + " " + strData[5]);
temp_el.SetAttributeValue("canEdit", "False");
temp_el.SetAttributeValue("status", nStatus.ToString());
temp_el.SetAttributeValue("qty", strData[13]);
temp_el.SetAttributeValue("part", strData[8]);
break;
}
}
Whilst the above works I am just curious as to why I am getting null returned. Is my workaround the best way of doing this?
Regards.
You defined your XElement like this:
x_element = new XElement("node", /* child nodes */);
Where "node" is the name of the XElement you are creating, and the following parameters are its children.
By using x_element.Node("node"), you are trying to get the child node named "node", and there isn't such a child node.
x_element itself is the node named "node".
DescendantsAndSelf worked because it includes x_element (hence "AndSelf"), but you don't need this either because you already have the node.
So you can change your second code snippet to this:
x_element.SetAttributeValue("title", strData[7] + " " + strData[6] + " " + strData[5]);
x_element.SetAttributeValue("canEdit", "False");
// etc.
(BTW, you can also add the Attributes in the constructor)
Because with your first temp_el,
XElement temp_el = x_element.Element("node");
You used to get nodes which is not treated to be an Element of x_element.
It was treated as its root. However, with the second one,
x_element.DescendantsAndSelf()`
You used this XElement Method which treat node itself as an element.
XContainer.Elements Method - Returns a collection of the child elements of this element or document, in document order.
XElement.DescendantsAndSelf Method - Returns a collection of elements that contain this element, and all descendant elements of this element, in document order.
To solve the issue I used Descendants(). Here is my code snippet
public void UpdateEnquiry([FromBody]XElement UpdatePurchaseOrder)
{
var obj = XElement.Parse(UpdatePurchaseOrder.ToString());
var ii = (from v in obj.Descendants() select new { v.Value }).ToList() ;
}

Getting the right node in Linq to XML

Im trying to parse an XML file containing all the uploaded videos on a certain channel. Im attempting to get tbe value of the URL attribute in one of the <media:content> nodes and put it in the ViewerLocation field. However there are several of them. My current code is this:
var videos = from xElem in xml.Descendants(atomNS + "entry")
select new YouTubeVideo()
{
Title = xElem.Element(atomNS + "title").Value,
Description = xElem.Element(atomNS + "content").Value,
DateUploaded = xElem.Element(atomNS + "published").Value,
ThumbnailLocation = xElem.Element(mediaNS + "group").Element(mediaNS + "content").Attribute("url").Value,
ViewerLocation = xElem.Element(mediaNS + "group").Element(mediaNS + "content").Attribute("url").Value
};
It gets me the first node in the XML for entry with the name <media:content> as you would expect. However, the first entry in the XML isn't what I want. I want the second.
Below is the relevant XML.
<!-- I currently get the value held in this node -->
<media:content
url='http://www.youtube.com/v/ZTUVgYoeN_b?f=gdata_standard...'
type='application/x-shockwave-flash' medium='video'
isDefault='true' expression='full' duration='215' yt:format='5'/>
<!-- What i actually want is this one -->
<media:content
url='rtsp://rtsp2.youtube.com/ChoLENy73bIAEQ1kgGDA==/0/0/0/video.3gp'
type='video/3gpp' medium='video'
expression='full' duration='215' yt:format='1'/>
<media:content
url='rtsp://rtsp2.youtube.com/ChoLENy73bIDRQ1kgGDA==/0/0/0/video.3gp'
type='video/3gpp' medium='video'
expression='full' duration='215' yt:format='6'/>
I want the second node because it has a type of 'video/3gpp'. How would I go about selecting that one? My logic would be
if attribute(type == "video/3gpp") get this value.
But i do not know how to express this in Linq.
Thanks,
Danny.
Probably something like;
where xElem.Element(atomNS + "content").Attribute("type").Value == "video/3gpp"
Edit: I didn't quite know how to expand and explain this one without assuming the OP had no knowledge of Linq. You want to make your original query;
from xElem in xml.Descendants(atomNS + "entry")
where xElem.Element(atomNS + "content").Attribute("type").Value == "video/3gpp"
select new YouTubeVideo() {
...
}
You can interrogate attributes of a node, just like you can look at the elements of the document. If there are multiple elements with that attribute, you could then (assuming you always want the first you find)..
( from xElem in xml.Descendants(atomNS + "entry")
where xElem.Element(atomNS + "content").Attribute("type").Value == "video/3gpp"
select new YouTubeVideo() {
...
}).First();
I changed the original post, as I believe the node you're querying is the Element(atomNS + "content"), not the top level xElem
Using XPath from this Xml Library (Just because I know how to use it) with associated Get methods:
string videoType = "video/3gpp";
XElement root = XElement.Load(file); // or .Parse(xmlstring)
var videos = root.XPath("//entry")
.Select(xElem => new YouTubeVideo()
{
Title = xElem.Get("title", "title"),
Description = xElem.Get("content", "content"),
DateUploaded = xElem.Get("published", "published"),
ThumbnailLocation = xElem.XGetElement("group/content[#type={0}]/url", "url", videoType),
ViewerLocation = xElem.XGetElement("group/content[#type={0}]/url", "url", videoType)
});
If the video type doesn't change, you can replace the XGetElement's with:
xElem.XGetElement("group/content[#type='video/3gpp']/url", "url")
Its a lot cleaner not having to specify namespaces using the library. There is the Microsoft's XPathSelectElements() and XPathSelectElement() you can look into, but they require you to specify the namespaces and don't have the nice Get methods imo. The caveat is that the library isn't a complete XPath implementation, but it does work with the above.

Categories

Resources