I'm currently using a loop which gives me a variable, which then needs to be fed into an Xpath method to get me any nodes with an attribute equal to my variable. So far, I've learned that Xpath allows you to select a node from the XML document using
root.SelectNodes("Element[#Attribute='SpecificValue']")
However, I'd like to know if there's a way I can insert a predefined variable where the specific value, so I can grab a different set of nodes with each iteration of my loop.
For example something like this:
string attribValue= "test"
root.SelectNodes("Element[#Attribute = attribValue]")
Use string formatting:
string attribValue = "test";
string expression = String.Format("Element[#Attribute = '{0}']", attribValue);
root.SelectNodes(expression);
Using XML Linq
XDocument doc = new XDocument();
XElement root = (XElement)doc.FirstNode;
string attribValue= "test";
var results = root.Descendants("Element").Where(x => x.Attribute("Attribute").Value == attribValue).ToList();
Related
Please see the XML below. Comparable_Sale is a repeating item. I am using new NewtonSoft’s Json parser and I need to be able to get the value of _Description attribute where the _type attribute equals a particular string value such as “GrossBuildingArea” from the element.
I have used the code below to parse property values with newtsonsoft, but I can’t figure out how to get at the attributes.
<COMPARABLE_SALE PropertySequenceIdentifier="3" ProjectName="Villages of Devinshire" ProjectPhaseIdentifier="1" PropertySalesAmount="132500" SalesPricePerGrossLivingAreaAmount="109.32" DataSourceDescription="FMLS, 5559496;DOM 80" DataSourceVerificationDescription="Tax Recs/2ndGen/Deeds" SalesPriceTotalAdjustmentPositiveIndicator="N" SalePriceTotalAdjustmentAmount="-1500" SalesPriceTotalAdjustmentGrossPercent="1.1" SalePriceTotalAdjustmentNetPercent="1.1" AdjustedSalesPriceAmount="131000">
<SALE_PRICE_ADJUSTMENT _Type="GrossBuildingArea"_Description="1,254"/>
<SALE_PRICE_ADJUSTMENT _Type="BasementArea" _Description="1,254 Sq.Ft."/>
<SALE_PRICE_ADJUSTMENT _Type="BasementFinish" _Description="1rr2ba4o"/>
</COMPARABLE_SALE>
This code gets me to the properties, but I can't see how to get at the attributes.
for each item in jobject.Children(Of JObject)()
For Each [property] In item.Children(Of JProperty)()
If [property].Value.Type = JTokenType.[String] Then
Dim newItem = New xmlRootValues()
newItem.Name = [property].Name
newItem.Value = [property].Value.ToString()
lstValues.Add(newItem)
End If
Next
next
Any help in either (C# or VB.net) would be much appreciated.
Thanks,
Chaos
To work with xml use an xml api like XDocument. Here you have an example in C#:
var xml = #"<COMPARABLE_SALE PropertySequenceIdentifier=""3"" ProjectName=""Villages of Devinshire"" ProjectPhaseIdentifier=""1"" PropertySalesAmount=""132500"" SalesPricePerGrossLivingAreaAmount=""109.32"" DataSourceDescription=""FMLS, 5559496;DOM 80"" DataSourceVerificationDescription=""Tax Recs/2ndGen/Deeds"" SalesPriceTotalAdjustmentPositiveIndicator=""N"" SalePriceTotalAdjustmentAmount=""-1500"" SalesPriceTotalAdjustmentGrossPercent=""1.1"" SalePriceTotalAdjustmentNetPercent=""1.1"" AdjustedSalesPriceAmount=""131000"">
<SALE_PRICE_ADJUSTMENT _Type=""GrossBuildingArea"" _Description=""1,254""/>
<SALE_PRICE_ADJUSTMENT _Type=""BasementArea"" _Description=""1,254 Sq.Ft.""/>
<SALE_PRICE_ADJUSTMENT _Type=""BasementFinish"" _Description=""1rr2ba4o""/>
</COMPARABLE_SALE>";
var xDoc = XDocument.Parse(xml);
var description = xDoc.Root.Elements("SALE_PRICE_ADJUSTMENT")
.First(e => e.Attribute("_Type").Value == "GrossBuildingArea")
.Attribute("_Description")
.Value;
In linq to xml you can also use Descendants and foreach to get all values for same element.
var obj = from item in xDoc.Descendants("SALE_PRICE_ADJUSTMENT")
select new
{
_Descr = item.Attribute("_Description").Value,
_Type = item.Attribute("_Type").Value
};
I am trying the below sample to select a node by ignoring the case and the select single node retuns null.
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><CHILD1>c1</CHILD1><CHILD2>c2</CHILD2></root>");
var node = doc.SelectSingleNode("root");
string nodeXpath = string.Format("//*[translate(#key, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{0}']","child1");
node = node.SelectSingleNode(nodeXpath);
string innertext = node.InnerText;
Can someone help.
#key in XPath means a reference to an attribute named key. There is no such attribute in your XML. If you meant to match by element name then you're supposed to use name() or local-name() instead :
...
string xpath = "//*[translate(name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{0}']";
string nodeXpath = string.Format(xpath,"child1");
...
You can use LINQ to Xml in little bid more readable way
XDocument doc = XDocument.Parse("<root><CHILD1>c1</CHILD1><CHILD2>c2</CHILD2></root>");
var singleNode =
doc.Root
.Elements()
.FirstOrDefault(element => element.Name.ToString().ToLower().Equals("child1"));
But notice that XML support different nodes where name can be case sensitive(for example "Node" and "node") and "searching" elements in "ignore case" way can lead to problems in the future.
I was working through this today and I used your solution. I just wrapped it in a function and call it whenever I need to match on an element name that's under the root. Works like a charm. Thanks!
private string GetNodeXpathCaseInsensitive(string value)
{
string xpath = String.Format("//*[translate(name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{0}']", value.ToLower());
return xpath;
}
I am trying to select values from elements using the XPathNodeIterator. But I am not able to get the expected value from my XML file.
My XML file
<?xml version="1.0" encoding="utf-8"?>
<ns0:Entity xmlns:ns0="http://schemas.mycompany.com/1.0">
<ns0:Data>
<Address_Id xmlns:ns0="http://schemas.mycompany.com/1.0">7</Address_Id>
<Customer_Id xmlns:ns0="http://schemas.mycompany.com/1.0">67</Customer_Id>
<CustomerName xmlns:ns0="http://schemas.mycompany.com/1.0">My Customer 1</CustomerName>
</ns0:Data>
</ns0:Entity>
My methods to get the values
I have created two methods which both doesn't return the values which I would like to have.
private static string GetValue_Attempt1(XPathDocument xPathDocument, string xpathExpression)
{
var xpathNavigator = xPathDocument.CreateNavigator();
var xpathNodeIterator = xpathNavigator.Select(xpathExpression);
xpathNodeIterator.MoveNext();
return xpathNodeIterator.Current.Value;
}
private static string GetValue_Attempt2(XPathDocument xPathDocument, string xpathExpression)
{
var xpathNavigator = xPathDocument.CreateNavigator();
var xpathNodeIterator = xpathNavigator.Select(xpathExpression);
xpathNodeIterator.MoveNext();
var nodesNavigator = xpathNodeIterator.Current;
var nodesText = nodesNavigator.SelectDescendants(XPathNodeType.Text, false);
nodesText.MoveNext();
return nodesText.Current.Value;
}
My XPath
Get Address_Id
XPath: //Data/Address_Id/text()
Attempt1 returns: 767My Customer 1. Why are all values concatenated ???
Attempt2 returns: 7. Looks OK
Get Customer_Id
XPath: //Data/Customer_Id/text()
Attempt1 returns: 767My Customer 1. Why are all values concatenated ???
Attempt2 returns: 7. Why do I get the Address_Id??
Questions
Probably I am using an incorrect XPath. But also I don't understand the results. In order to understand whats going on. I would like to know:
Is there a better way (method) to get the value of the elements?
What is the XPath string which I need to use?
Why does method GetValue_Attempt1 return a concatinated string?
Why does method GetValue_Attempt2 return the value of only the first element?
Your XPath returns no matches as you're not taking the namespaces into account.
Your 'Attempt 1' calls MoveNext() once, so Value returns all the concatenated text in Data, and in 'Attempt 2' you call MoveNext() twice which positions you inside the Address_Id element.
In addition, Select, even it the XPath was valid, doesn't actually move the cursor.
If you wanted to get the value, you'd do something like this - note I'm including the namespace in the XPath and using the result of SelectSingleNode:
var nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("ns0", "http://schemas.mycompany.com/1.0");
var value = navigator.SelectSingleNode("//ns0:Data/Address_Id", nsm).Value;
However, do you have any reason to be using XPath and XPathDocument here? Using the much more expressive and (more) statically typed LINQ to XML would be far preferable, I'd think:
XNamespace ns = "http://schemas.mycompany.com/1.0";
var doc = XDocument.Parse(xml);
var addressId = (int)doc.Descendants(ns + "Data")
.Elements("Address_Id")
.Single();
var customerId = (int)doc.Descendants(ns + "Data")
.Elements("Customer_Id")
.Single();
I have this XML:
<palinsesto>
<giorno label="Mer" data="2014/12/31">
<canale description="Premium Cinema" id="KE">
<prg Pod="N" Nettv="N" orafine="06:30" orainizio="06:00" replica="No" primaTv="No">
<durata duratapixel="30">30</durata>
<tipologia>Type</tipologia>
<titolo>evento iniziato ieri</titolo>
<descrizione>--</descrizione>
<audio sottotitoli="No subtitles" audioType="Mono" doppioAudio="One language">Not used</audio>
<parentalRating>LIBERO DA DIVIETI</parentalRating>
<trafficLight/>
<anno>--</anno>
<paese>--</paese>
and i need to read the value in prg class and palinsesto class, i try in this mode but not work
XDocument doc = XDocument.Parse(e.Result);
var canal = doc.Descendants(XName.Get("description", "canale")).FirstOrDefault();
var date = doc.Descendants(XName.Get("data", "giorno")).FirstOrDefault();
var title = doc.Descendants(XName.Get("titolo", "prg")).FirstOrDefault();
return always error
It looks like you've misunderstood names, attributes and elements. It looks like you just want something like:
XDocument doc = XDocument.Parse(e.Result);
var root = doc.Root;
var canal = root.Element("canale").Attribute("description").Value;
var date = root.Element("giorno").Attribute("data").Value;
var title = root.Element("titolo").Value;
However:
Currently none of your first three elements are closed, which would cause the above to fail; it's not clear what your real XML would look like. You should indent it to show the intended structure.
Your date is not represented in the normal way for XML - if you're in control of the XML, it would be better to have a value of 2014-12-31
The above code assumes you just want the first element from the root. If that's not the case, you'll need to give us more information
You do not require this XName overload here - the second parameter is used to provide a namespace, which isn't present in the xml you've provided. I believe you are confusing attributes and xmlns namespaces. In order to obtain the attributes, use .Attributes(), as follows:
var canal = doc.Descendants("canale").Attributes("description").FirstOrDefault();
var date = doc.Descendants("giorno").Attributes("data").FirstOrDefault();
var title = doc.Descendants("prg").Attributes("titolo").FirstOrDefault();
Given this XML:
<InitResponse>
<LottoToken>908ec70b308adf10d04db1478ef9b01b</LottoToken>
<GameInfoList>
<GameInfo>
<Draw>
<gameId>L649</gameId>
<draw>3035</draw>
</Draw>
</GameInfo>
<GameInfo>
<Draw>
<gameId>BC49</gameId>
<draw>2199</draw>
</Draw>
</GameInfo>
</GameInfoList>
</InitResponse>
I need to get the draw number based on a specific gameId. For example if I specify gameID L649 I need to get 3035.
The following works in several online evaluators, but not in C#. It says it cannot find it. Suggestions?
/InitResponse/GameInfoList/GameInfo/Draw/draw[preceding-sibling::gameId='L649']
C# Code I've tried:
XmlNode node = xmlDoc.SelectSingleNode("/InitResponse/GameInfoList/GameInfo/Draw/draw[preceding-sibling::gameId='L649']");
... where xmlDoc is an xmlDocument object loaded with the xml. the node variable ends up with a null value which seems to indicate there was no match found.
Here is xpath (with Linq)
var xdoc = XDocument.Load(path_to_xml);
string xpath = "/InitResponse/GameInfoList/GameInfo/Draw[gameId='L649']/draw";
var draw = xdoc.XPathSelectElement(xpath);
if (draw != null) // check if draw with gameId found in xml
value = (int)draw;
Also you can use pure Linq to Xml (but in this case xpath looks more compact):
var draw = xdoc.Descendants("GameInfo")
.SelectMany(g => g.Elements("Draw"))
.SingleOrDefault(d => (string)d.Element("gameId") == "L649");
if (draw != null)
value = (int)draw.Element("draw");
Using XmlDocument
I didn't saw something wrong in your XPath statement, look on the following:
(So i guess there is something else that is wrong)
XmlDocument myDoc = new XmlDocument();
String str = #"<InitResponse>
<LottoToken>908ec70b308adf10d04db1478ef9b01b</LottoToken>
<GameInfoList>
<GameInfo>
<Draw>
<gameId>L649</gameId>
<draw>3035</draw>
/Draw>
</GameInfo>
<GameInfo>
<Draw>
<gameId>BC49</gameId>
<draw>2199</draw>
</Draw>
</GameInfo>
</GameInfoList>
</InitResponse>";
myDoc.LoadXml(str);
XmlNode node =
myDoc.SelectSingleNode("/InitResponse/GameInfoList/GameInfo/Draw/draw[preceding-sibling::gameId='L649']");
The node which returns from the result is: 3035
Note: your first note have to be <InitResponse> otherwise it will returns null