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();
}
}
}
Related
I can't parse the attribute value from this file, I used Linq and the standard library. Object reference error. Need attribut's value "RequeryNumber".
XML file
Excel Query:
exctract = XML.DocumentElement
.SelectSingleNode("//KPOKS/ReestrExtract/DeclarAttribute")
.Attributes
.getNamedItem("RequeryNumber").Text
SelectSingleNode() uses XPath to find XML nodes that fulfill the filter.
There are several ways to get what you want. If RequeryNumber only exists on one node, you could do like this:
var attribute = xDoc.DocumentElement
.SelectSingleNode("//*[#RequeryNumber]/#RequeryNumber");
MessageBox.Show("text" + attribute.InnerText);
[#RequeryNumber] finds the node which has an attribute named "RequeryNumber".
/#RequeryNumber extracts that specific attribute, on which you can get the value using the InnerText property.
I would suggest you take a look at: XPath cheatsheet
I had one error on following line in the xml that I fixed
I parsed below your xml and needed to add the namespace to get the XElement using Xml Linq.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XNamespace ns = doc.Root.GetDefaultNamespace();
XElement declarAttribute = doc.Descendants(ns + "DeclarAttribute").FirstOrDefault();
}
}
}
I have an XML file, similar to the one below. I will read the XML and find the EmpId node. Based on other data will determine if I need to keep this node or delete the node. Currently have a process which reads it and creates a list of Record Ids which I will need to remove. In the below XML I will want to keep the EmpId = Emp1 and remove EmpId = Emp2. The removal is the node.
I believe the best approach is to read the XML first to determine which nodes to keep and then go thru the XML again and remove the necessary nodes.
What's the best approach to remove these nodes?
I'm open to creating a new XML document and creating the node which need to be kept. Based on the data I'm reading it's 50/50 if I'll be removing more nodes or keeping more nodes.
...
<HeaderNode>
<Details>
<SubmissionId>1</SubmissionId>
<EmpDetail>
<RecordId>1</RecordId>
<EmpDempgraphic>
<EmpId>Emp1</EmpId>
</EmpDempgraphic>
</EmpDetail>
<EmpDetail>
<RecordId>2</RecordId>
<EmpDempgraphic>
<EmpId>Emp2</EmpId>
</EmpDempgraphic>
</EmpDetail>
</Details>
</HeaderNode>
...
Using Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace XML
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement EmpDetail = doc.Descendants("EmpDetail").Where(x => (int)x.Element("RecordId") == 2).FirstOrDefault();
EmpDetail.Remove();
}
}
}
Unless the document is huge, just load it into an XDocument, Remove the elements you don't want, and Save the document back out.
Essentially, I have tried everything and for some reason I can't get the value of the elements in my XML based off the parameter I need it to meet. I feel like I'm close but I just don't know where I'm going wrong. I'm trying to get the value of the elements and put them into a list to be used elsewhere. Currently it doesn't put anything in the list.
I've tried XML Reader so now I'm giving Linq to XML a try but this won't work either.
private List<string> outputPath = new List<string>();
var doc = XDocument.Load(Path.Combine(projectDirectory, "JobPaths.xml"));
foreach (var child in doc.Element("Jobs").Elements("Job").Where(x => x.Attribute("Name").ToString() == jobName).Elements())
{
outputPath.Add(child.Name.ToString());
}
return outputPath;
Here's the XML:
<?xml version="1.0" encoding="utf-8" ?>
<Jobs>
<Job Name="events_monitoring_c">
<Path>\\stadb4412\</Path>
</Job>
<Job Name="events_monitoring_d">
<Path>\\stadb4412\</Path>
<Path>\\stadb1111\</Path>
<Path>\\stadb2412\</Path>
</Job>
</Jobs>
The jobName comes from the XML File, so I'm trying to get all the path elements based on the job name, regardless of how many there are. I want to get all the paths in the list to be used elsewhere.
To find nodes of a specific type/tag from an XDocument or XElement you use .Descendants(name), then you have .Attribute(name) that returns an XAttribute. To get its value, you use .Value, not .ToString().
Your code gets the Job elements, but then it gets the children elements as an IEnumerable of nodes and for each of them adds the Name of the tags, which is always Path.
What you are looking for is doc.Descendants("Job").Where(job=>job.Attribute("Name")?.Value==jobName).SelectMany(job=>job.Elements()).Select(elem=>elem.Value).ToList();
I did it without compiling, so I may be wrong.
You parse into a dictionary using Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Dictionary<string, List<string>> dict = doc.Descendants("Job")
.GroupBy(x => (string)x.Attribute("Name"), y => y)
.ToDictionary(x => x.Key, y => y.Elements("Path").Select(z => (string)z).ToList());
}
}
}
I'm new to both XML and C#; I'm trying to find a way to efficiently parse a given xml file to retrieve relevant numerical values, base on the "proj_title" value=heat_run or any other possible values. For example, calculating the duration of a particular test run (proj_end val-proj_start val).
ex.xml:
<proj ID="2">
<proj_title>heat_run</proj_title>
<proj_start>100</proj_start>
<proj_end>200</proj_end>
</proj>
...
We can't search by proj ID since this value is not fixed from test run to test run. The above file is huge: ~8mb, and there's ~2000 tags w/ the name proj_title. is there an efficient way to first find all tag names w/ proj_title="heat_run", then to retrieve the proj start and end value for this particular proj_title using C#??
Here's my current C# code:
public class parser
{
public static void Main()
{
XmlDocument xmlDoc= new XmlDocument();
xmlDoc.Load("ex.xml");
//~2000 tags w/ proj_title
//any more efficient way to just look for proj_title="heat_run" specifically?
XmlNodeList heat_run_nodes=xmlDoc.GetElementsByTagName("proj_title");
}
}
8MB really isn't very large at all by modern standards. Personally I'd use LINQ to XML:
XDocument doc = XDocument.Load("ex.xml");
var projects = doc.Descendants("proj_title")
.Where(x => (string) x == "heat_run")
.Select(x => x.Parent) // Just for simplicity
.Select(x => new {
Start = (int) x.Element("proj_start"),
End = (int) x.Element("proj_end")
});
foreach (var project in projects)
{
Console.WriteLine("Start: {0}; End: {1}", project.Start, project.End);
}
(Obviously adjust this to your own requirements - it's not really clear what you need to do based on the question.)
Alternative query:
var projects = doc.Descendants("proj")
.Where(x => (string) x.Element("proj_title") == "heat_run")
.Select(x => new {
Start = (int) x.Element("proj_start"),
End = (int) x.Element("proj_end")
});
You can use XPath to find all nodes that match, for example:
XmlNodeList matches = xmlDoc.SelectNodes("proj[proj_title='heat_run']")
matches will contain all proj nodes that match the critera. Learn more about XPath: http://www.w3schools.com/xsl/xpath_syntax.asp
MSDN Documentation on SelectNodes
Use XDocument and use the LINQ api.
http://msdn.microsoft.com/en-us/library/bb387098.aspx
If the performance is not what you expect after trying it, you have to look for a sax parser.
A Sax parser will not load the whole document in memory and try to apply an xpath expression on everything in memory. It works more in an event driven approach and in some cases this can be a lot faster and does not use as much memory.
There are probably sax parsers for .NET around there, haven't used them myself for .NET but I did for C++.
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.