Just when I was thinking that I had Linq To Xml sussed I'm faced with yet another error! I think if I was to understand the linq search process in general better I might have more success, so any good links regarding that are also welcome. To my problem however; using the code below:
Dim xd As XDocument = _
<?xml version="1.0" encoding="utf-8"?>
<root>
<element>
<subelement id="1"/>
<subelement id="2"/>
<subelement id="3"/>
</element>
<element>
<subelement id="4"/>
<subelement id="1"/>
<subelement id="5"/>
</element>
</root>
Dim results = _
From q In xd.Descendants.<element> _
Where q.<subelement>.#id = 1
For Each xe As XElement In results
Console.WriteLine(xe.ToString)
Next
I would have expected the above code to return both 'element' nodes, but it only returns the first because it only searches the first 'subelement' node within 'element', how can I make the where clause apply to all 'subelement' nodes?
You could do
Dim results = _
From q In xd.Descendants.<element> _
From p In q.<subelement> _
Where p.#id = 1
(That is, if my VB.NET is up to the task here... I could do it in C#. Please feel free to edit.)
Related
Ive read a few articles on Linq to XML and either ive picked it up wrong or missing some piece of the puzzle.
What im trying to achieve is to load some XML, get required data by different named fields and nodes/elements. Here is the XML
<?xml version="1.0" encoding="utf-8"?>
<metadata created="2014-05-15T12:26:07.701Z" xmlns="http://site/cu-2.0#" xmlns:ext="http://site/cu/b-2.0">
<customer-list count="47" offset="0">
<customer id="7123456" type="Cust" ext:mark="1">
<name>Tony Watt</name>
<sort-name>Watt, Tony</sort-name>
<gender>male</gender>
<country>US</country>
<knownAs-list>
<knownAs locale="ko" sort-name="Tony Watt"</knownAs>
<knownAs locale="ja" sort-name="Watt Tony"</knownAs>
</knownAs-list>
</customer>
<tag-list>
<tag count="1">
<name>Country</name>
</tag>
<tag count="1">
<name>usa</name>
</tag>
<customer id="9876543" type="Cust" ext:mark="2">
So i can load the XML and i can display data. Heres a snippet of the code
Dim ns As XNamespace = "http://site/cu-2.0#"
Dim XDoc As XDocument = XDocument.Parse(SomeXML)
For Each c As XElement In XDoc.Descendants(ns + "name")
Response.Write(c)
Next
So this displays all the elements with "name". The problem i have here is i want the customer name but not the tag-list country name (see last few lines of the XML)
Ideally i want to return all the details for each customer but adding the namespace limits the me to all the elements with name when i want other data too. If i remove the namespace i get no results returned so im unsure what to do next?
Ive read a ton of articles but i cant seem to work out what needs to be done or if ive gone down the wrong path? Please remember i have tried other methods which i can post if anyone likes but after reading MSDN and other articles i think ive confused myself or missed out a step.
I think you simply want to use
Dim ns As XNamespace = "http://site/cu-2.0#"
Dim XDoc As XDocument = XDocument.Parse(SomeXML)
For Each c As XElement In XDoc.Descendants(ns + "customer")
Response.Write(c.Element(ns + "name").Value)
Next
If you are trying to get name from customer give this a try
Dim xe As XElement =
<customer-list count="47" offset="0">
<customer id="7123456" type="Cust" mark="1">
<name>Tony Watt</name>
<sort-name>Watt, Tony</sort-name>
<gender>male</gender>
<country>US</country>
<knownAs-list>
<knownAs locale="ko" sort-name="Tony Watt"></knownAs>
<knownAs locale="ja" sort-name="Watt Tony"></knownAs>
</knownAs-list>
</customer>
<customer id="1" type="Cust" mark="1">
<name>Fred Flintstone</name>
<sort-name>Flintstone, Fred</sort-name>
<gender>male</gender>
<country>US</country>
<knownAs-list>
<knownAs locale="ko" sort-name="Fred Flintstone"></knownAs>
<knownAs locale="ja" sort-name="Flintstone Fred"></knownAs>
</knownAs-list>
</customer>
<tag-list>
<tag count="1">
<name>Country</name>
</tag>
<tag count="1">
<name>usa</name>
</tag>
</tag-list>
</customer-list>
Dim ie As IEnumerable(Of XElement) = From c As XElement In xe.Elements
Where c.Name.LocalName = "customer"
Select c From n As XElement In c.Elements
Where n.Name.LocalName = "name" Select n
For Each r As XElement In ie
Debug.WriteLine(r.Value)
Next
<code>
For Each oXElement In oXDocument.Descendants("searchResult")
sTitle = oXElement.Element("title").Value
Next
</code>
I have also tried:
<code>
For Each oXElement In oXDocument.Elements(searchResults)
sTitle = oXElement.Element("title").Value
Next
</code>
I am having trouble getting a hold of nodes as well as understanding the way you communicate with XDocument nodes.
My Ultimate goal is to create an Ebay Object Model From all Ebay Element's Attributes. For that I need to refer to XML tag somehow - and this is where I would appreciate your advice or sample example that could let me proceed with parsing out this XML response.
Thank you all much for any help.
PS: I have searched for a similar questions and found a few of the same kind but still could not get my parsing to work.
<findItemsByProductResponse xmlns="http://www.ebay.com/marketplace/search/v1/services">
<ack>Success</ack>
<version>1.12.0</version>
<timestamp>2013-06-02T22:42:04.500Z</timestamp>
<searchResult count="5">
<item>
<itemId>370821427802</itemId>
<title>
Modern Database Management 11E by Hoffer, Ramesh, Topi 11th (Int'l Edition)
</title>
<globalId>EBAY-US</globalId>
<primaryCategory>
<categoryId>2228</categoryId>
<categoryName>Textbooks, Education</categoryName>
</primaryCategory>
<galleryURL>
http://thumbs3.ebaystatic.com/m/meSAqCRbXecSjZjO1833dWQ/140.jpg
</galleryURL>
<viewItemURL>
http://www.ebay.com/itm/Modern-Database-Management-11E-Hoffer-Ramesh-Topi-11th-Intl-Edition-/370821427802?pt=US_Texbook_Education
</viewItemURL>
<productId type="ReferenceID">143649496</productId>
<paymentMethod>PayPal</paymentMethod>
<autoPay>true</autoPay>
<location>Malaysia</location>
<country>MY</country>
<shippingInfo>
<shippingServiceCost currencyId="USD">0.0</shippingServiceCost>
<shippingType>Free</shippingType>
<shipToLocations>Worldwide</shipToLocations>
<expeditedShipping>true</expeditedShipping>
<oneDayShippingAvailable>false</oneDayShippingAvailable>
<handlingTime>1</handlingTime>
</shippingInfo>
<sellingStatus>
<currentPrice currencyId="USD">54.07</currentPrice>
<convertedCurrentPrice currencyId="USD">54.07</convertedCurrentPrice>
<sellingState>Active</sellingState>
<timeLeft>P20DT10H47M20S</timeLeft>
</sellingStatus>
<listingInfo>
<bestOfferEnabled>false</bestOfferEnabled>
<buyItNowAvailable>false</buyItNowAvailable>
<startTime>2013-05-24T09:25:25.000Z</startTime>
<endTime>2013-06-23T09:29:24.000Z</endTime>
<listingType>StoreInventory</listingType>
<gift>false</gift>
</listingInfo>
<returnsAccepted>true</returnsAccepted>
<condition>
<conditionId>1000</conditionId>
<conditionDisplayName>Brand New</conditionDisplayName>
</condition>
<isMultiVariationListing>false</isMultiVariationListing>
<topRatedListing>true</topRatedListing>
</item>
<item>...</item>
<item>...</item>
<item>...</item>
<item>...</item>
</searchResult>
<paginationOutput>
<pageNumber>1</pageNumber>
<entriesPerPage>5</entriesPerPage>
<totalPages>3</totalPages>
<totalEntries>14</totalEntries>
</paginationOutput>
<itemSearchURL>
http://www.ebay.com/ctg/143649496?LH_BIN=1&_ddo=1&_incaucbin=0&_ipg=5&_pgn=1
</itemSearchURL>
</findItemsByProductResponse>
You have to use XNamespace instance when querying your XML:
Dim ns = XNamespace.Get("http://www.ebay.com/marketplace/search/v1/services")
And with that add it to every Descendants, Elements, Element, Attributes, Attributes, etc. calls you make:
For Each oXElement In oXDocument.Descendants(ns + "searchResult")
sTitle = oXElement.Element(ns + "title").Value
Next
For Each oXElement In oXDocument.Elements(ns + searchResults)
sTitle = oXElement.Element(ns + "title").Value
Next
Two things. First, you fell into the trap that catches 90% of the people with problems using LINQ to XML. You forgot the namespace. You can use the following which works in C# or VB:
Dim ns = XNamespace.Get("http://www.ebay.com/marketplace/search/v1/services")
VB Also lets you use a Imports for a namespace just as you import other .Net namespaces at the top of your file. The advantage of this option is that if you have a schema in your project, you get intellisense over the XML structure while building your query.
Imports <xmlns:eb="http://www.ebay.com/marketplace/search/v1/services">
The second issue you have is that the title element is not a direct child of searchResult, but is nested an additional level deeper. Here's a sample leveraging the imports for the namespace. I'm using the VB XML Literals for descendents (...) for contrast with anyone giving you a C# biased answer ;-)
Public Class XmlTest
Public Sub TestXml()
Dim data = <findItemsByProductResponse xmlns="http://www.ebay.com/marketplace/search/v1/services">
<ack>Success</ack>
<version>1.12.0</version>
<timestamp>2013-06-02T22:42:04.500Z</timestamp>
<searchResult count="5">
<item>
<itemId>370821427802</itemId>
<title>
Modern Database Management 11E by Hoffer, Ramesh, Topi 11th (Int'l Edition)
</title>
<globalId>EBAY-US</globalId>
<primaryCategory>
<categoryId>2228</categoryId>
<categoryName>Textbooks, Education</categoryName>
</primaryCategory>
<galleryURL>
http://thumbs3.ebaystatic.com/m/meSAqCRbXecSjZjO1833dWQ/140.jpg
</galleryURL>
<viewItemURL>
http://www.ebay.com/itm/Modern-Database-Management-11E-Hoffer-Ramesh-Topi-11th-Intl-Edition-/370821427802?pt=US_Texbook_Education
</viewItemURL>
<productId type="ReferenceID">143649496</productId>
<paymentMethod>PayPal</paymentMethod>
<autoPay>true</autoPay>
<location>Malaysia</location>
<country>MY</country>
<shippingInfo>
<shippingServiceCost currencyId="USD">0.0</shippingServiceCost>
<shippingType>Free</shippingType>
<shipToLocations>Worldwide</shipToLocations>
<expeditedShipping>true</expeditedShipping>
<oneDayShippingAvailable>false</oneDayShippingAvailable>
<handlingTime>1</handlingTime>
</shippingInfo>
<sellingStatus>
<currentPrice currencyId="USD">54.07</currentPrice>
<convertedCurrentPrice currencyId="USD">54.07</convertedCurrentPrice>
<sellingState>Active</sellingState>
<timeLeft>P20DT10H47M20S</timeLeft>
</sellingStatus>
<listingInfo>
<bestOfferEnabled>false</bestOfferEnabled>
<buyItNowAvailable>false</buyItNowAvailable>
<startTime>2013-05-24T09:25:25.000Z</startTime>
<endTime>2013-06-23T09:29:24.000Z</endTime>
<listingType>StoreInventory</listingType>
<gift>false</gift>
</listingInfo>
<returnsAccepted>true</returnsAccepted>
<condition>
<conditionId>1000</conditionId>
<conditionDisplayName>Brand New</conditionDisplayName>
</condition>
<isMultiVariationListing>false</isMultiVariationListing>
<topRatedListing>true</topRatedListing>
</item>
<item>...</item>
<item>...</item>
<item>...</item>
<item>...</item>
</searchResult>
<paginationOutput>
<pageNumber>1</pageNumber>
<entriesPerPage>5</entriesPerPage>
<totalPages>3</totalPages>
<totalEntries>14</totalEntries>
</paginationOutput>
</findItemsByProductResponse>
For Each el In data...<eb:searchResult>
Console.WriteLine(el...<eb:title>.Value)
Next
End Sub
End Class
I would like to insert a new element in the following XML for each instance of a data node
Here is the initial XML that I have:
<dataCollection totalCount="12" pageCount="1">
<data>
<date>2011-11-10T00:00:00.000-05:00</date>
<dataType>PRCP</dataType>
<station>GHCND:USW00014739</station>
<value>267</value>
<address>
<home>X</home>
</address>
</data>
<data>
<date>2011-11-10T00:00:00.000-05:00</date>
<dataType>PRCP</dataType>
<station>GHCND:USW00014739</station>
<value>267</value>
<address>
<home>X</home>
</address>
</data>
</dataCollection>
And this is the XML I am trying to achieve
<dataCollection totalCount="12" pageCount="1">
<data>
<date>2011-11-10T00:00:00.000-05:00</date>
<dataType>TMIN</dataType>
<station>GHCND:USW00014739</station>
<value>267</value>
<newValue>60</newValue>
<address>
<home>X</home>
</address>
</data>
<data>
<date>2011-11-10T00:00:00.000-05:00</date>
<dataType>TMAX</dataType>
<station>GHCND:USW00014739</station>
<value>270</value>
<newValue>62</newValue>
<address>
<home>X</home>
</address>
</data>
</dataCollection>
The XML data is used as a data source for a DataGrid using the following Linq.
Dim elements = (From daDsc In xdoc.Descendants("data") _
Select Data_Type = daDsc.Element("dataType").Value, _
Raw_Value = daDsc.Element("value").Value,
newValue = daDsc.Element("newValue"))
Writing this in VB.net, but answers in C# is OK. Thanks.
Try this:
foreach (var xe in xml.Descendants("data"))
{
xe.Element("value")
.AddAfterSelf(new XElement("newValue", 42));
}
If you wish to add newValue into your XML before processing, you could do something like this:
For Each element As XElement In xml.Elements
element.SetElementValue("newValue", "something")
Next
where xml is an XDocument, loaded from your web service.
I have this XML:
<root>
<data name="lnkViewResultResource1.Text" xml:space="preserve">
<value>bekijk de resultaten</value>
</data>
<data name="lnkVoteResource1.Text" xml:space="preserve">
<value>stem</value>
</data>
<data name="number of results" xml:space="preserve">
<value>er waren reeds {0} stemmen op deze poll {1}</value>
</data>
</root>
I want to retrieve the "data" values, and for this I used:
Public Shared Function getlabels(ByVal filename As String) As Array
Dim labels = From l In XElement.Load(filename).Descendants("root").Elements("data") Select l
Return labels.ToArray
But, unfortunately, it is not working, it is not getting anything.
Any ideas?
Thanks in advance.
Alf.
You're loading the <root> XElement and asking it for descendants called root. Just get rid of the Descendants("root") call and it should be okay.
On the other hand, the query expression is pretty pointless... just use:
Return XElement.Load(filename).Elements("data").ToArray
Try
Dim labels = From l In XElement.Load(filename).Descendants("data") Select l
I have a class called Profile that has some simple properties and then it can have a collection of ProfileItem that again has some simple properties and then it can have a collection of ProfileItem (RECURSION).
Now I am trying to generated a very simple save function using XML Literals that come with VB.NET (3.5).
The code I am using is the following:
Dim xdoc As XDocument = _
<?xml version="1.0" encoding="utf-8"?>
<profiles>
<%= _
From p In _Profiles _
Select <profile name=<%= p.Name %>>
<%= _
From i In p.GetProfileItems _
Select <item>
<name><%= i.Name %></name>
<action><%= i.Action.ToString %></action>
<type><%= i.Type.ToString %></type>
<arguments><%= i.Arguments %></arguments>
<dependencies>
<%= _
From d In i.GetDependencies _
Select <dependency>
<name><%= d.Name %></name>
</dependency> _
%>
</dependencies>
</item> _
%>
</profile> _
%>
</profiles>
The part related to tag should become recursive, but I don't know if it is in some way supported by this syntax.
Should I rewrite all avoiding usage of XML Literal to implement recursion?
Recursion is one of the reasons I love VB.NET XML Literals!
In order to do the recursion, you need a function that accepts a ProfileItems collection and returns an XElement. Then you can recursively call that function inside your XML Literal.
Also, in order for the recursion to work, GetProfileItems and GetDependencies need to have the same name (rename one of them) and display with the same Xml Element structure. Here's what the recursive function might look like:
Function GetProfileItemsElement(ByVal Items As List(Of ProfileItem) As XElement
Return <items>
<%= From i In Items _
Select <item>
<name><%= i.Name %></name>
<!-- other elements here -->
<%= GetProfileItemsElement(i.GetDependencies) %>
</item> %>
</items>
End Function
The recursion will end when you get to an item that returns an empty list for the GetDependencies function. In that case, the nested items element will be empty: <items/>. XML Literals are smart enough to combine the start and end items tags when there aren't any child elements.