I have this code :
/*string theXml =
#"<Response xmlns=""http://myvalue.com""><Result xmlns:a=""http://schemas.datacontract.org/2004/07/My.Namespace"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""><a:TheBool>true</a:TheBool><a:TheId>1</a:TheId></Result></Response>";*/
string theXml = #"<Response><Result><TheBool>true</TheBool><TheId>1</TheId></Result></Response>";
XDocument xmlElements = XDocument.Parse(theXml);
var elements = from data in xmlElements.Descendants("Result")
select new {
TheBool = (bool)data.Element("TheBool"),
TheId = (int)data.Element("TheId"),
};
foreach (var element in elements)
{
Console.WriteLine(element.TheBool);
Console.WriteLine(element.TheId);
}
When I use the first value for theXml, the result is null, whereas with the second one, I have good values ...
How to use Linq to Xml with xmlns values ?
LINQ to XML methods like Descendants and Element take an XName as an argument. There is a conversion from string to XName that is happening automatically for you. You can fix this by adding an XNamespace before the strings in your Descendants and Element calls. Watch out because you have 2 different namespaces at work.
string theXml =
#"true1";
//string theXml = #"true1";
XDocument xmlElements = XDocument.Parse( theXml );
XNamespace ns = "http://myvalue.com";
XNamespace nsa = "http://schemas.datacontract.org/2004/07/My.Namespace";
var elements = from data in xmlElements.Descendants( ns + "Result" )
select new
{
TheBool = (bool) data.Element( nsa + "TheBool" ),
TheId = (int) data.Element( nsa + "TheId" ),
};
foreach ( var element in elements )
{
Console.WriteLine( element.TheBool );
Console.WriteLine( element.TheId );
}
Notice the use of ns in Descendants and nsa in Elements
You can pass an XName with a namespace to Descendants() and Element(). When you pass a string to Descendants(), it is implicitly converted to an XName with no namespace.
To create a XName in a namespace, you create a XNamespace and concatenate it to the element local-name (a string).
XNamespace ns = "http://myvalue.com";
XNamespace nsa = "http://schemas.datacontract.org/2004/07/My.Namespace";
var elements = from data in xmlElements.Descendants( ns + "Result")
select new
{
TheBool = (bool)data.Element( nsa + "TheBool"),
TheId = (int)data.Element( nsa + "TheId"),
};
There is also a shorthand form for creating a XName with a namespace via implicit conversion from string.
var elements = from data in xmlElements.Descendants("{http://myvalue.com}Result")
select new
{
TheBool = (bool)data.Element("{http://schemas.datacontract.org/2004/07/My.Namespace}TheBool"),
TheId = (int)data.Element("{http://schemas.datacontract.org/2004/07/My.Namespace}TheId"),
};
Alternatively, you could query against XElement.Name.LocalName.
var elements = from data in xmlElements.Descendants()
where data.Name.LocalName == "Result"
I have several namespaces listed at the top of an XML document, I don't really care about which elements are from which namespace. I just want to get the elements by their names. I've written this extension method.
/// <summary>
/// A list of XElement descendent elements with the supplied local name (ignoring any namespace), or null if the element is not found.
/// </summary>
public static IEnumerable<XElement> FindDescendants(this XElement likeThis, string elementName) {
var result = likeThis.Descendants().Where(ele=>ele.Name.LocalName==elementName);
return result;
}
I found the following code to work fine for reading attributes with namespaces in VB.NET:
MyXElement.Attribute(MyXElement.GetNamespaceOfPrefix("YOUR_NAMESPACE_HERE") + "YOUR_ATTRIB_NAME")
Hope this helps someone down the road.
Related
I have a xml file:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationConfiguration xmlns="http://test.org/SDK/Configuration.xsd">
<ApplicationName>
<ApplicationUri>123</ApplicationUri>
<ApplicationUri>456</ApplicationUri>
</ApplicationName>
</ApplicationConfiguration>
What I want is set the value of ApplicationUri from 456 to 789 in C# code.
I wrote this code:
string docaddress = "testfile.xml";
XDocument doc = XDocument.Load(docaddress);
doc.Element("ApplicationConfiguration")
.Elements("ApplicationName").FirstOrDefault()
.SetElementValue("ApplicationUri", "789");
doc.Save(docaddress);
The problems are:
There is no error while running. I think the element ApplicationConfiguration is not correct. But when I delete the line xmlns=... from the xml file, it runs normally
The value 789 is replaced with 123, but not 456 as I want (same element name)
Can you tell me how to fix those problems?
Hello and welcome to Stack Overflow!
The xmlns attribute on the ApplicationConfiguration element makes that the root.
So you get the root first. Then you replace the values of each descendant, selecting locally by name with the element name you want. something like this:
string docaddress = "C:\\temp\\testfile.xml";
XDocument doc = XDocument.Load(docaddress);
var root = doc.Root;
var descendants = root.Descendants();
var these = root.Descendants().Where(p => p.Name.LocalName == "ApplicationUri");
foreach (var elem in these)
{
elem.Value = "789";
}
doc.Save(docaddress);
I added namespace to your code :
string docaddress = "testfile.xml";
XDocument doc = XDocument.Load(docaddress);
XNamespace ns = doc.Root.GetDefaultNamespace();
doc.Element(ns + "ApplicationConfiguration")
.Elements(ns + "ApplicationName").FirstOrDefault()
.SetElementValue(ns + "ApplicationUri", "789");
doc.Save(docaddress);
IMHO, here is an easiest method.
It is taking care of the XML default namespace.
No loops. Set based approach.
c#
void Main()
{
const string fileName = #"e:\temp\hala.xml";
const string searchFor = "456";
const string replaceWith = "789";
XDocument doc = XDocument.Load(fileName);
XNamespace ns = doc.Root.GetDefaultNamespace();
// step #1: find element based on the search value
XElement xmlFragment = doc.Descendants(ns + "ApplicationUri")
.Where(d => d.Value.Equals(searchFor)).FirstOrDefault();
// step #2: if found, set its value
if(xmlFragment != null)
xmlFragment.SetValue(replaceWith);
doc.Save(fileName);
}
I have this code :
/*string theXml =
#"<Response xmlns=""http://myvalue.com""><Result xmlns:a=""http://schemas.datacontract.org/2004/07/My.Namespace"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""><a:TheBool>true</a:TheBool><a:TheId>1</a:TheId></Result></Response>";*/
string theXml = #"<Response><Result><TheBool>true</TheBool><TheId>1</TheId></Result></Response>";
XDocument xmlElements = XDocument.Parse(theXml);
var elements = from data in xmlElements.Descendants("Result")
select new {
TheBool = (bool)data.Element("TheBool"),
TheId = (int)data.Element("TheId"),
};
foreach (var element in elements)
{
Console.WriteLine(element.TheBool);
Console.WriteLine(element.TheId);
}
When I use the first value for theXml, the result is null, whereas with the second one, I have good values ...
How to use Linq to Xml with xmlns values ?
LINQ to XML methods like Descendants and Element take an XName as an argument. There is a conversion from string to XName that is happening automatically for you. You can fix this by adding an XNamespace before the strings in your Descendants and Element calls. Watch out because you have 2 different namespaces at work.
string theXml =
#"true1";
//string theXml = #"true1";
XDocument xmlElements = XDocument.Parse( theXml );
XNamespace ns = "http://myvalue.com";
XNamespace nsa = "http://schemas.datacontract.org/2004/07/My.Namespace";
var elements = from data in xmlElements.Descendants( ns + "Result" )
select new
{
TheBool = (bool) data.Element( nsa + "TheBool" ),
TheId = (int) data.Element( nsa + "TheId" ),
};
foreach ( var element in elements )
{
Console.WriteLine( element.TheBool );
Console.WriteLine( element.TheId );
}
Notice the use of ns in Descendants and nsa in Elements
You can pass an XName with a namespace to Descendants() and Element(). When you pass a string to Descendants(), it is implicitly converted to an XName with no namespace.
To create a XName in a namespace, you create a XNamespace and concatenate it to the element local-name (a string).
XNamespace ns = "http://myvalue.com";
XNamespace nsa = "http://schemas.datacontract.org/2004/07/My.Namespace";
var elements = from data in xmlElements.Descendants( ns + "Result")
select new
{
TheBool = (bool)data.Element( nsa + "TheBool"),
TheId = (int)data.Element( nsa + "TheId"),
};
There is also a shorthand form for creating a XName with a namespace via implicit conversion from string.
var elements = from data in xmlElements.Descendants("{http://myvalue.com}Result")
select new
{
TheBool = (bool)data.Element("{http://schemas.datacontract.org/2004/07/My.Namespace}TheBool"),
TheId = (int)data.Element("{http://schemas.datacontract.org/2004/07/My.Namespace}TheId"),
};
Alternatively, you could query against XElement.Name.LocalName.
var elements = from data in xmlElements.Descendants()
where data.Name.LocalName == "Result"
I have several namespaces listed at the top of an XML document, I don't really care about which elements are from which namespace. I just want to get the elements by their names. I've written this extension method.
/// <summary>
/// A list of XElement descendent elements with the supplied local name (ignoring any namespace), or null if the element is not found.
/// </summary>
public static IEnumerable<XElement> FindDescendants(this XElement likeThis, string elementName) {
var result = likeThis.Descendants().Where(ele=>ele.Name.LocalName==elementName);
return result;
}
I found the following code to work fine for reading attributes with namespaces in VB.NET:
MyXElement.Attribute(MyXElement.GetNamespaceOfPrefix("YOUR_NAMESPACE_HERE") + "YOUR_ATTRIB_NAME")
Hope this helps someone down the road.
I have this xml string:
<a:feed xmlns:a="http://www.w3.org/2005/Atom"
xmlns:os="http://a9.com/-/spec/opensearch/1.1/"
xmlns="http://schemas.zune.net/catalog/apps/2008/02">
<a:link rel="self" type="application/atom+xml" href="/docs" />
<a:updated>2014-02-12</a:updated>
<a:title type="text">Chickens</a:title>
<a:content type="html">eat 'em all</a:content>
<sortTitle>Chickens</sortTitle>
... other stuffs
<offers>
<offer>
<offerId>8977a259e5a3</offerId>
... other stuffs
<price>0</price>
... other stuffs
</offer>
</offers>
... other stuffs
</a:feed>
and want to get value of <price> but here in my codes:
XDocument doc = XDocument.Parse(xmlString);
var a = doc.Element("a");
var offers = a.Element("offers");
foreach (var offer in offers.Descendants())
{
var price = offer.Element("price");
var value = price.Value;
}
doc.Element("a"); returns null. I tried removing that line offers is also null. what is wrong in my code and how to get value of price? thanks
Here is correct way to get prices:
var xdoc = XDocument.Parse(xmlString);
XNamespace ns = xdoc.Root.GetDefaultNamespace();
var pricres = from o in xdoc.Root.Elements(ns + "offers").Elements(ns + "offer")
select (int)o.Element(ns + "price");
Keep in mind that your document have default namespace, and a is also namespace.
Get the namespace somehow, like
XNameSpace a = doc.Root.GetDefaultNamespace();
or, probably better:
XNameSpace a = doc.Root.GetNamespaceOfPrefix("a");
and then use it in your queries:
// to get <a:feed>
XElement f = doc.Element(a + "feed");
You can also set the namespace from a literal string, but then avoid var.
var xDoc = XDocument.Load(filename);
XNamespace ns = "http://schemas.zune.net/catalog/apps/2008/02";
var prices = xDoc
.Descendants(ns + "offer")
.Select(o => (decimal)o.Element(ns + "price"))
.ToList();
a is a namespace. To get the feed element try this:
XDocument doc = XDocument.Parse(xmlString);
XNamespace a = "http://www.w3.org/2005/Atom";
var feed = doc.Element(a + "feed");
So I have this code:
List<PriceDetail> prices =
(from item in xmlDoc.Descendants(shop.DescendantXName)
select new PriceDetail
{
Price = GetPrice(item.Element(shop.PriceXPath).Value),
GameVersion = GetGameVersion(((IEnumerable)item.XPathEvaluate(shop.TitleXPath)).Cast<XAttribute>().First<XAttribute>().Value, item.Element(shop.PlatformXPath).Value),
Shop = shop,
Link = item.Element(shop.LinkXPath).Value,
InStock = InStock(item.Element(shop.InStockXPath).Value)
}).ToList<PriceDetail>();
The problem I have is this code:
((IEnumerable)item.XPathEvaluate(shop.TitleXPath)).Cast<XAttribute>().First<XAttribute>().Value
Sometimes the object from XPathEvaluate could be XElement and then the casting doesn't work. So what I need is a Cast that works with both XAttribute and XElement.
Any suggestion?
Change your XPath expression (shop.TitleXPath) from:
someXPathExpression
to:
string(someXPathExpression)
Then you can simplify the code to just:
string result = item.XPathEvaluate(shop.TitleXPath) as string;
Complete working example:
using System;
using System.IO;
using System.Xml.Linq;
using System.Xml.XPath;
class TestXPath
{
static void Main(string[] args)
{
string xml1 =
#"<t>
<a b='attribute value'/>
<c>
<b>element value</b>
</c>
<e b='attribute value'/>
</t>";
string xml2 =
#"<t>
<c>
<b>element value</b>
</c>
<e b='attribute value'/>
</t>";
TextReader sr = new StringReader(xml1);
XDocument xdoc = XDocument.Load(sr, LoadOptions.None);
string result1 = xdoc.XPathEvaluate("string(/*/*/#b | /*/*/b)") as string;
TextReader sr2 = new StringReader(xml2);
XDocument xdoc2 = XDocument.Load(sr2, LoadOptions.None);
string result2 = xdoc2.XPathEvaluate("string(/*/*/#b | /*/*/b)") as string;
Console.WriteLine(result1);
Console.WriteLine(result2);
}
}
When this program is executed, the same XPath expression is applied on two different XML documents and, regardless of the fact that the argument to string() is an attribute the first time and is an element on the second, we get the correct results -- written to the Console:
attribute value
element value
Dimitre's solution returns empty string if the element is not found; we can't distinguish it from actual empty value. So I had to make this extension method that handles multiple results by XPath query and returns empty enumeration if nothing is found:
public static IEnumerable<string> GetXPathValues(this XNode node, string xpath)
{
foreach (XObject xObject in (IEnumerable)node.XPathEvaluate(xpath))
{
if (xObject is XElement)
yield return ((XElement)xObject).Value;
else if (xObject is XAttribute)
yield return ((XAttribute)xObject).Value;
}
}
XElement and XAttribute are both forms of XObject, so if a generic instance of type XObject will suffice for your needs, change your Cast<XAttribute> to Cast<XObject>.
If that won't work for your specific situation, you make use of OfType<XAttribute> or OfType<XElement> to filter for one or the other, but that would require two passes over the input, one to filter for XElement and a second pass to filter for XAttribute.
Before you make the cast you can check for the type using a code like this:
XElement e = item as XElement;
XAttribute a = item as XAttribute;
if(e != null)
//item is of type XElement
else
//item is of type XAttribute
I get the followinng Xresponse after parsing the XML document:
<DIDL-Lite
xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/">
<item id="1182" parentID="40" restricted="1">
<title>Hot Issue</title>
</item>
As per the earlier thread, When there is a default namespace in the document, you must parse it as if it were a named namespace. For example.
XNamespace ns = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
var xDIDL = xResponse.Element(ns + "DIDL-Lite");
But in my case I have four different name space. I am not getting any results after using the following query , I am getting the response , Not Yeilding any results:
XNamespace dc = "http://purl.org/dc/elements/1.1/";
var vAudioData = from xAudioinfo in xResponse.Descendants(ns + "DIDL-lite").Elements("item")
select new RMSMedia
{
strAudioTitle = ((string)xAudioinfo.Element(dc + "title")).Trim(),
};
I have no clue whats going on as am new to it. Please help
This is because your item element is in your "ns" namespace. Use:-
XNamespace dc = "http://purl.org/dc/elements/1.1/";
XName didl = ns + "DIDL-lite";
XName item = ns + "item";
XName title = dc + "title";
var vAudioData = from xAudioinfo in xResponse.Descendants(didl).Elements(item)
select new RMSMedia
{
strAudioTitle = ((string)xAudioinfo.Element(title)).Trim(),
};
In these cases I tend to create myself a private class to hold the set of XNames I need to simplify the query code.
You are not getting any results because you are using the wrong namespace. All elements without prefix are in the namespace urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/.
Items in the namespace http://purl.org/dc/elements/1.1/ are prefixed with dc: in the xml document. The fragment does not show any items so it is hart to tell what elements you are looking for.
For example - given the following xml:
<DIDL-Lite
xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/">
<item id="1182" parentID="40" restricted="1">
<title>Hot Issue</title>
<dc:title>Purl Title</dc:title>
</item>
</DIDL-Lite>
And also given the assumption that you want to retrieve both titles the following code should yiedl the results you are looking for:
XNamespace dc= "http://purl.org/dc/elements/1.1/";
XNamespace ns = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
var result = xAudioinfo.Descendants(ns + "title"); // <title></title>
var result2 = xAudioinfo.Descendants(dc + "title"); // <dc:title></dc:title>