It's been a while since I have worked with ASP.Net and C#. I am trying to parse an XML API using C# and I am in need of some help. My Problem is I am not quite sure how to do this. I keep seeing conflicting methods too. Some show like I did below. Some show pretty awesome queries that to me look way more better.
Example of query
IEnumerable<string> partNos =
from item in purchaseOrder.Descendants("Item")
select (string) item.Attribute("PartNumber");
Which method is better and how do I achieve parsing the XML just to a textbox for now?
Here is the XML format:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<findItemsByKeywordsResponse xmlns="http://www.ebay.com/marketplace/search/v1/services">
<ack>Success</ack>
<version>1.12.0</version>
<timestamp>2012-06-20T22:20:33.539Z</timestamp>
<searchResult count="1">
<item>
<itemId>390432965446</itemId>
<title>
Yamaha RX-V673 7.2 Channel 90 Watt Aventage Receiver {Brand New}
</title>
<globalId>EBAY-US</globalId>
<primaryCategory>
<categoryId>14981</categoryId>
<categoryName>Home Theater Receivers</categoryName>
</primaryCategory>
<galleryURL>
http://thumbs3.ebaystatic.com/pict/3904329654464040_1.jpg
</galleryURL>
<viewItemURL>
http://www.ebay.com/itm/Yamaha-RX-V673-7-2-Channel-90-Watt-Aventage-Receiver-Brand-New-/390432965446?pt=Receivers_Tuners
</viewItemURL>
<productId type="ReferenceID">114468754</productId>
<paymentMethod>PayPal</paymentMethod>
<autoPay>false</autoPay>
<postalCode>54143</postalCode>
<location>Marinette,WI,USA</location>
<country>US</country>
<shippingInfo>
<shippingServiceCost currencyId="USD">0.0</shippingServiceCost>
<shippingType>Free</shippingType>
<shipToLocations>US</shipToLocations>
<expeditedShipping>false</expeditedShipping>
<oneDayShippingAvailable>false</oneDayShippingAvailable>
<handlingTime>2</handlingTime>
</shippingInfo>
<sellingStatus>
<currentPrice currencyId="USD">519.0</currentPrice>
<convertedCurrentPrice currencyId="USD">519.0</convertedCurrentPrice>
<sellingState>Active</sellingState>
<timeLeft>P28DT23H32M35S</timeLeft>
</sellingStatus>
<listingInfo>
<bestOfferEnabled>false</bestOfferEnabled>
<buyItNowAvailable>false</buyItNowAvailable>
<startTime>2012-06-19T21:48:08.000Z</startTime>
<endTime>2012-07-19T21:53:08.000Z</endTime>
<listingType>StoreInventory</listingType>
<gift>false</gift>
</listingInfo>
<returnsAccepted>true</returnsAccepted>
<condition>
<conditionId>1000</conditionId>
<conditionDisplayName>New</conditionDisplayName>
</condition>
<isMultiVariationListing>false</isMultiVariationListing>
</item>
</searchResult>
<paginationOutput>
<pageNumber>1</pageNumber>
<entriesPerPage>1</entriesPerPage>
<totalPages>1121495</totalPages>
<totalEntries>1121495</totalEntries>
</paginationOutput>
<itemSearchURL>
http://www.ebay.com/sch/i.html?_nkw=yamaha&_ddo=1&_ipg=1&_pgn=1
</itemSearchURL>
</findItemsByKeywordsResponse>
My C# Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Linq;
namespace ebayLinq
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string myAppID = "hidden from stack overflow";
string ebayUrl = "http://svcs.ebay.com/services/search/FindingService/v1?";
string operationName = "OPERATION-NAME=getSearchKeywordsRecommendation&";
string serviceVersion = "SERVICE-VERSION=1.11.0&";
string securityAppName = "SECURITY-APPNAME="+ myAppID +"&";
string responseData = "RESPONSE-DATA-FORMAT=XML&";
string rest = "REST-PAYLOAD&";
string searchString = "macbook Pro";
string keywords ="keywords="+searchString+"&";
var xml = XDocument.Load(ebayUrl +
operationName +
serviceVersion +
securityAppName +
responseData);
//XNamespace ns = "http://www.ebay.com/marketplace/search/v1/services";
//XElement ack = xml.Root.Element(ns + "ack");
}
}
}
Okay so as you can I can get it to work with the code above, but I dont know how to go deeper than the ack so far. I also would rather do queries as opposed to the method used above.
Any input friends?
With your input I came up with this but it doesn't work right?
XElement convertedCurrentPrice = (from x in xml.Root.Descendants("title") select x).FirstOrDefault();
string item = Convert.ToString(convertedCurrentPrice);
TextBox1.Text = item;
I'm not personally familiar with older methods of parsing XML but the new LINQ to XML stuff is the query style that you are talking about and definitely makes things quick and easy when it comes to pulling information out of your XDocument.
If you give me an example of one or more of the nodes that you want to pull data from in particular I can help you out with that but the basic structure would be (say if you wanted to grab the current price value of 519.0)
XElement convertedCurrentPrice = (from x in xml.Root.Descendants("convertedCurrentPrice") select x).FirstOrDefault();
This will return the entire XML node (everything in between and
Then getting the value is as simple as:
double price = Convert.ToDecimal(convertedCurrentPrice.Value);
It's hard to answer which syntax is better. For reference, they are called Query Syntax and Method Syntax. Here is a the MSDN article on the differences (the article recommends query syntax due to readability). They are roughly equivalent, and I use both of them frequently.
For further reference, here is an SO question discussing the topic.
Related
I've been trying forever using Descendants, Elements and attributes to retrieve data from an XML file that I'm writing to save metadata. I can't at the moment and it's blocking my work. When I was using Linq to XML, I was yielding no value whatsoever and I couldn't under why happening.
Quick look into the xml file
<ImageMetadata>
<ColorHistogram>
<Bin value="45861"/>
<Bin value="31989"/>
</ColorHistogram/>
<FaceLocations>
<FacePosition Y="379" X="205"/>
<FacePosition Y="366" X="372"/>
</FaceLocations>
</ImageMetadata>
I've tried different solutions. At first, I only had an XElement called BinValue instead of the tag Bin with the attribute value which lead to this code:
//Yielding no results
from elements in doc.Descendants()
let element = elements.Element("BinValue")
select (long)element;
Then after getting mad at LINQ to XML, I changed the structure a bit of my document to have a tag and an attribute. But this have no effect whatsoever.
var bins = XElement.Load(dbMetadata)
.Descendants("Bin")
.Select(e => e.Attribute("value").Value);
// which gives me : System.ArgumentException: 'Illegal characters in path.'
Getting values from xelement
My use case has you could have gather from the xml structure is the following: creating metadata of an image file. That portion looks pretty solid with OpenCV and that's not my issue. Maybe in order to get more feedback on my issue, it'd be relevant to add the code I was using to build my XML document.
The portion that computes data on an image was done using F#. The part that creates the xml document was done using C#. Because of that, I'm going to create two gists to share my code. Please, remember to add the Emgu OpenCV nugget package in your solutions.
Image Analyzer - F#
XMLDocumentParser - C#
**Use any two local jpg files in order to run the F# code that will generate the metadata !
**If possible, I'd like a way to retrieve the data using LINQ to XML. For both ColorHistogram and FaceLocations
UPDATE1
I was asked in the comments to show the state of the xml file when my problems occurred. You can find it below:
Metadata file
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication49
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("ImageMetadata").Select(x => new
{
colorHistograms = x.Descendants("ColorHistogram").Select(y => new
{
bin = y.Elements("Bin").Select(z => new
{
value = (int)z.Attribute("value")
}).ToList()
}).FirstOrDefault(),
faceLocations = x.Descendants("FaceLocations").Select(y => new
{
facePosition = y.Elements("FacePosition").Select(z => new
{
X = (int)z.Attribute("X"),
Y = (int)z.Attribute("Y")
}).ToList()
}).FirstOrDefault()
}).FirstOrDefault();
}
}
}
I got XML code like this:
<Body>
<Schoolyear>2016</Schoolyear>
<ClassLeader>
<Id>200555</Id>
<Name>Martin</Name>
<Short>ma</Short>
</ClassLeader>
<Info>
some very useful information :)
</Info>
</Body>
I only need one tag, e. g. SchoolYear
I tried this:
foreach (XElement element in Document.Descendants("Schoolyear"))
{
myDestinationVariable = element.Value;
}
It works, but I think maybe there is a more performant and easier solution.
You can take it using LINQ or just use Element with the specified XName
Add namespace
using System.Xml.Linq;
And use one of these examples
string xml = #"<Body>
<Schoolyear>2016</Schoolyear>
<ClassLeader>
<Id>200555</Id>
<Name>Martin</Name>
<Short>ma</Short>
</ClassLeader>
<Info>
some very useful information :)
</Info>
</Body>";
XDocument dox = XDocument.Parse(xml);
var exampl1 = dox.Element("Body").Element("Schoolyear").Value;
var exampl2 = dox.Descendants().FirstOrDefault(d => d.Name == "Schoolyear").Value;
This question already has answers here:
How to parse very huge XML Files in C#? [duplicate]
(2 answers)
Closed 6 years ago.
I want to read data from XML file but i do not want to read all data and then sort and filter. basically i do not want to load all XML data into memory rather want to read small amount of data by pagination.
we often read data from database with pagination and same way i want to read data from xml file with pagination. so tell me what would be the best class which i can use to do it.
My xml data look like:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Orders>
<OrderID>10248</OrderID>
<CustomerID>VINET</CustomerID>
<EmployeeID>5</EmployeeID>
<OrderDate>1996-07-04T00:00:00</OrderDate>
<RequiredDate>1996-08-01T00:00:00</RequiredDate>
<ShippedDate>1996-07-16T00:00:00</ShippedDate>
<ShipVia>3</ShipVia>
<Freight>32.3800</Freight>
<ShipName>Vins et alcools Chevalier</ShipName>
<ShipAddress>59 rue de l'Abbaye</ShipAddress>
<ShipCity>Reims</ShipCity>
<ShipPostalCode>51100</ShipPostalCode>
<ShipCountry>France</ShipCountry>
</Orders>
</Root>
now reading like this way with LINQ
XDocument document = XDocument.Load(#"c:\users\WindowsFormsApplication5\Orders.xml");
var query = from r in document.Descendants("Orders")
select new
{
OrderID = r.Element("OrderID").Value,
CustomerID = r.Element("CustomerID").Value,
EmployeeID = r.Element("EmployeeID").Value,
};
//setup query result ordering,
//assume we have variable to determine ordering mode : bool isDesc = true/false
if (isDesc) query = query.OrderByDescending(o => o.OrderID);
else query = query.OrderBy(o => o.OrderID);
//setup pagination,
//f.e displaying result for page 2 where each page displays 100 data
var page = 2;
var pageSize = 100;
query = query.Skip(page - 1*pageSize).Take(pageSize);
//execute the query to get the actual result
var items = query.ToList();
If anyone see my above code then must notice the way I am reading data from XML file. It load all data into memory but I suppose I want to read 10 data at time then just will load 10 data in memory not all.
Above coding approach will not be good if there is 10,000,000 records in XML file. So, what would be best way to read large XML file partly with pagination like concept.
Use XmlReader
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication16
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
while(!reader.EOF)
{
if(reader.Name != "Orders")
{
reader.ReadToFollowing("Orders");
}
if(!reader.EOF)
{
XElement order = (XElement)XElement.ReadFrom(reader);
int OrderID = (int)order.Element("OrderID");
string CustomerID = (string)order.Element("CustomerID");
int EmployeeID = (int)order.Element("EmployeeID");
}
}
}
}
}
I have the following XML:
<Events>
<Event>
<EventID displayName="Event ID">1</EventID>
<EventName displayName="Event Name">Some event</EventName>
<OrgID displayName="Organization ID">8</OrgID>
</Event>
<Event>
<EventID displayName="Event ID">2</EventID>
<EventName displayName="Event Name">Another Event</EventName>
<OrgID displayName="Organization ID">10</OrgID>
</Event>
</Events>
I want to be able to filter them by a where clause constructed dynamically. For example:
Where("Event ID = 2 AND (Organization ID = 8 OR Organization ID = 10)")
Please note that I can only use the displayName to filter the data. Obviously I can get the tag name from the displayName and construct the where clause, but that means some added calculations on the clients machine, which if possible, I'd like to avoid.
I have explored the options to use Dynamic Linq or DataTable.Select() but with my limited knowledge on Linq, I cant seem to find an easy way to adopt those to filter XML data. Any help/hint is appreciated.
Using LINQ-to-XML dot notation to extract the elements:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
string content = #"<Events>
<Event>
<EventID displayName=""Event ID"">1</EventID>
<EventName displayName=""Event Name"">Some event</EventName>
<OrgID displayName=""Organization ID"">8</OrgID>
</Event>
<Event>
<EventID displayName=""Event ID"">2</EventID>
<EventName displayName=""Event Name"">Another Event</EventName>
<OrgID displayName=""Organization ID"">10</OrgID>
</Event>
</Events>";
XDocument doc = XDocument.Load(new System.IO.StringReader(content));
var events = doc.Descendants("Event")
.Where(p => p.Elements("EventID").First().Value == "1")
.Where(p => p.Elements("OrgID").First().Value == "8" || p.Elements("OrgID").First().Value == "10");
}
}
}
The System.XML.Linq namespace is incredibly useful.
Parse the xml completely
var eventList=doc.Elements("Event")
.Select(x=>new
{
EventId=int.Parse(x.Element("EventId").Value),
EventName=x.Element("EventName").Value,
OrgID=int.Parse(x.Element("OrgID").Value)
});
You could now make a generalized method,
public string getEventName(int eventId,params int[] orgId)
{
return eventList.Where(x=>
x.EventId==eventId &&
orgId.Any(y=>y==x.OrgID))
.Select(x.EventName)
.Single();
}
Now you can do
getEventName(2);
getEventName(2,8);
getEventName(2,8,10);
So want you want to do is to use linq syntax to get data from your xml ?
Something like that?
XDocument loadedXML = XDocument.Load('Your xml file name');
var yourStrongTypeEventsList
= (from event in loadedXML.Descendants("Event")
where event.Element("EventID").Attribute("displayName").Value.Equals("Your value here")
select new
{
EventID = event.Element("EventID").Value,
EventName = event.Element("EventName").Value
}).ToList();
I have a string :
responsestring = "<?xml version="1.0" encoding="utf-8"?>
<upload><image><name></name><hash>SOmetext</hash>"
How can i get the value between
<hash> and </hash>
?
My attempts :
responseString.Substring(responseString.LastIndexOf("<hash>") + 6, 8); // this sort of works , but won't work in every situation.
also tried messing around with xmlreader , but couldn't find the solution.
ty
Try
XDocument doc = XDocument.Parse(str);
var a = from hash in doc.Descendants("hash")
select hash.Value;
you will need System.Core and System.Xml.Linq assembly references
Others have suggested LINQ to XML solutions, which is what I'd use as well, if possible.
If you're stuck with .NET 2.0, use XmlDocument or even XmlReader.
But don't try to manipulate the raw string yourself using Substring and IndexOf. Use an XML API of some description. Otherwise you will get it wrong. It's a matter of using the right tool for the job. Parsing XML properly is a significant chunk of work - work that's already been done.
Now, just to make this a full answer, here's a short but complete program using your sample data:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string response = #"<?xml version='1.0' encoding='utf-8'?>
<upload><image><name></name><hash>Some text</hash></image></upload>";
XDocument doc = XDocument.Parse(response);
foreach (XElement hashElement in doc.Descendants("hash"))
{
string hashValue = (string) hashElement;
Console.WriteLine(hashValue);
}
}
}
Obviously that will loop over all the hash elements. If you only want one, you could use doc.Descendants("hash").Single() or doc.Descendants("hash").First() depending on your requirements.
Note that both the conversion I've used here and the Value property will return the concatenation of all text nodes within the element. Hopefully that's okay for you - or you could get just the first text node which is a direct child if necessary.
var val = XElement.Parse();
val.Descendants(...).Value
Get your xml well formed and escape the double quotes with backslash. Then apply the following code
XDocument resp = XDocument.Parse("<hash>SOmetext</hash>");
var r= from element in resp.Elements()
where element.Name == "hash"
select element;
foreach (var item in r)
{
Console.WriteLine(item.Value);
}
You can use an xmlreader and/or xpath queries to get all desired data.
XmlReader_Object.ReadToFollowing("hash");
string value = XmlReader_Object.ReadInnerXml();