I don't know too much about this subject yet, so I'm open to any suggestions that lead to my end goal...
I receive an XML string from a web service
I would like to load the string into a C# class capable of being used for LINQ
I would like to be able to extract an array of all "someentity" instances in the XML using LINQ with that class.
Here's some sample XML I made up with the someentity example:
<replydata>
<someentity>
<role id="1234" roletype="2" />
<history length="24" accessstr="http://someurl" />
</someentity>
<someentity>
<role id="1235" roletype="2" />
<history length="30" accessstr="http://someurl2" />
</someentity>
... keep repeating for a while
</replydata>
Is this possible, and if so, can someone provide a simple example or direct me to the right place to find one?
You can do it like this:
var responseString =
#"<replydata>
<someentity>
<role id=""1234"" roletype=""2"" />
<history length=""24"" accessstr=""http://someurl"" />
</someentity>
<someentity>
<role id=""1235"" roletype=""2"" />
<history length=""30"" accessstr=""http://someurl2"" />
</someentity>
</replydata>";
var response = XElement.Load(new StringReader(responseString));
var someentitys = response.Elements("someentity");
foreach(var e in someentitys) {
Console.WriteLine(
"Role: {0}, access: {1}"
, e.Element("role").Attribute("roletype")
, e.Element("history").Attribute("accessstr")
);
}
The class you're looking for here is XDocument
http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.aspx
You can create an XDocument from an XML String using it's Parse() method and then use LINQ to query that document.
Here's an article that demos its LINQ Capabilities
http://broadcast.oreilly.com/2010/10/understanding-c-simple-linq-to.html
Related
I have an xml document that I want to obtain attributes from
Here is the XML:
<Translations>
<Product Name="Room" ID="16">
<Terms>
<Term Generic="Brand" Product="Sub Category" />
<Term Generic="Range" Product="Brand" />
</Terms>
</Product>
<Product Name="House"" ID="29">
<Terms>
<Term Generic="Category" Product="Product Brand" />
<Term Generic="Brand" Product="Category Description" />
<Term Generic="Range" Product="Group Description" />
<Term Generic="Product" Product="Product Description" />
</Terms>
</Product>
</Translations>
Here is my current Linq query
public static string clsTranslationTesting(string GenericTerm, int ProductID)
{
const string xmlFilePath = "C:\\Dev\\XMLTrial\\XMLFile1.xml";
var xmlDocument = XDocument.Load(xmlFilePath);
var genericValue =
from gen in xmlDocument.Descendants("Product")
where gen.Attribute("ID").Value == ProductID.ToString()
select gen.Value.ToString();
}
The error that I am having is when I pass data into the method, the method loads the xml from the file to the xmlDocument variable successfully. However when it executes the query it returns a value null. I want to obtain the ID value.
I'm a little lost with your question, but here's my attempt.
First thing is you need to change "Customer" to "Product". Your XML contains not a single instance of the word "Customer" so I think you have a typo there.
I don't know exactly what you want returned from the query (I assume just the entire matched node?). Try this:
var genericValue = xmlDocument.Descendants("Product")
.FirstOrDefault(x => x.Attribute("ID").Value == "16");
I made a fiddle here that shows it in action
I'm trying to read in some XML data but I'm getting an error I've never spotted before. Can anyone shed some light on this?
The error:
Screen name: System.Xml.Linq.Extensions+c__Iterator5
UnityEngine.Debug:Log(Object)
HV_ReadSettingsFile:Update() (at Assets/_scripts/HV_ReadSettingsFile.cs:64)
Here is my code reading in the XML and is ultimately generating the error:
var xdoc = XDocument.Load(#"C:\\Test.xml");
var screen = xdoc.Descendants("Screen");
foreach (var Screen in screen)
{
HV_Screen _screen = new HV_Screen();
_screen.Name = Convert.ToString(Screen.Descendants("Name").Attributes("Name"));
Debug.Log("Screen name: " + _screen.Name);
}
And my XML sheet:
<Settings>
<Display_Settings>
<Screen>
<Name Name="Screen" />
<ScreenTag Tag="Screen Tag" />
<LocalPosition X="12" Y="81" Z="28" />
<Width Width="54" />
<Height Height="912" />
</Screen>
<Screen>
<Name Name="Screen" />
<ScreenTag Tag="Screen Tag" />
<LocalPosition X="32" Y="21" Z="28" />
<Width Width="54" />
<Height Height="912" />
</Screen>
</Display_Settings>
</Settings>
That's not an error. That's logging exactly what you've asked for. You've asked for all the Name attributes in all the Name elements... but then you're trying to log that sequence as a single value.
I think you want:
var screens = xdoc.Descendants("Screen");
foreach (var screen in screens)
{
HV_Screen _screen = new HV_Screen();
_screen.Name = (string) screen.Element("Name").Attribute("Name");
Debug.Log("Screen name: " + _screen.Name);
}
This is now looking for one element and one attribute per Screen element. Note that I've changed the variable names to follow normal .NET conventions, other than _screen as I don't know what HV_Screen is meant to be.
It's not really clear why you've got separate elements for Width, Height and Name, each with an attribute matching the element name - and ScreenTag nearly doing so. If the XML is under your control, you might consider either:
<Screen Name="Screen" Width="54" Height="912" Tag="Screen Tag">
<LocalPosition X="12" Y="81" Z="28" />
</Screen>
or perhaps:
<Screen>
<Name>Screen</Name>
<ScreenTag>Screen Tag</ScreenTag>
<LocalPosition X="12" Y="81" Z="28" />
<Width>54</Width>
<Height>912</Height>
</Screen>
Also, if you're trying to build a collection of HV_Screen items, you probably want to use a query rather than a foreach loop - but it's not clear what you're actually trying to do beyond logging debug information at the moment.
You can try to do:
var xdoc = XDocument.Load(#"C:\\test.xml");
List<string> lstScreen = xdoc.Descendants("Screen").Select(x => x.Element("Name").Attribute("Name").Value).ToList();
foreach (string name in lstScreen)
{
...
I hope help you!!
Below is my response generated from a webservice.
I want to do such that I want only PresentationElements node from this response.
Any help how can I achieve this query?
<?xml version="1.0"?>
<GetContentResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ExtensionData />
<GetContentResult>
<ExtensionData />
<Code>0</Code>
<Value>Success</Value>
</GetContentResult>
<PresentationElements>
<PresentationElement>
<ExtensionData />
<ContentReference>Product View Pack</ContentReference>
<ID>SHOPPING_ELEMENT:10400044</ID>
<Name>View Pack PE</Name>
<PresentationContents>
<PresentationContent>
<ExtensionData />
<Content>View Pack</Content>
<ContentType>TEXT</ContentType>
<Language>ENGLISH</Language>
<Medium>COMPUTER_BROWSER</Medium>
<Name>Name</Name>
</PresentationContent>
<PresentationContent>
<ExtensionData />
<Content>Have more control of your home's security and lighting with View Pack from XFINITY Home.</Content>
<ContentType>TEXT</ContentType>
<Language>ENGLISH</Language>
<Medium>COMPUTER_BROWSER</Medium>
<Name>Description</Name>
</PresentationContent>
<PresentationContent>
<ExtensionData />
<Content>/images/shopping/devices/xh/view-pack-2.jpg</Content>
<ContentType>TEXT</ContentType>
<Language>ENGLISH</Language>
<Medium>COMPUTER_BROWSER</Medium>
<Name>Image</Name>
</PresentationContent>
<PresentationContent>
<ExtensionData />
<Content>The View Pack includes:
2 Lighting / Appliance Controllers
2 Indoor / Outdoor Cameras</Content>
<ContentType>TEXT</ContentType>
<Language>ENGLISH</Language>
<Medium>COMPUTER_BROWSER</Medium>
<Name>Feature1</Name>
</PresentationContent>
</PresentationContents>
</PresentationElement>
</PresentationElements>
</GetContentResponse>
You can use XPath extensions
var xdoc = XDocument.Parse(response);
XElement presentations = xdoc.XPathSelectElement("//PresentationElements");
You may use the System.Xml.Linq.XDocument:
//Initialize the XDocument
XDocument doc = XDocument.Parse(yourString);
//your query
var desiredNodes = doc.Descendants("PresentationElements");
Pretty easy, have you tried:
XDocument xml = XDocument.Load("... xml");
var nodes = (from n in xml.Descendants("PresentationElements")
select n).ToList();
You could also project each individual node to an anonymous type using something like:
select new
{
ContentReference = (string)n.Element("ContentReference").Value,
.... etc
}
I'm trying to find the inner text value of an element using LINQ-to-XML (an XElement object). I make my service call and get an XML response back that I've successfully loaded into an XElement object. I want to extract the inner text of one of the elements - however, every time I try to do this, I get a null result.
I feel like I'm missing something super-simple, but I'm fairly new to LINQ-to-XML. Any help is appreciated.
I'm trying to get the inner text value of the StatusInfo/Status element. Here's my XML document that's returned:
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom">
<title type="text">My Response</title>
<id>tag:foo.com,2012:/bar/06468dfc-32f7-4650-b765-608f2b852f22</id>
<author>
<name>My Web Services</name>
</author>
<link rel="self" type="application/atom+xml" href="http://myServer/service.svc/myPath" />
<generator uri="http://myServer" version="1">My Web Services</generator>
<entry>
<id>tag:foo.com,2012:/my-web-services</id>
<title type="text" />
<updated>2012-06-27T14:22:42Z</updated>
<category term="tag:foo.com,2008/my/schemas#system" scheme="tag:foo.com,2008/my/schemas#type" />
<content type="application/vnd.my.webservices+xml">
<StatusInfo xmlns="tag:foo.com,2008:/my/data">
<Status>Available</Status> <!-- I want the inner text -->
</StatusInfo>
</content>
</entry>
</feed>
Here's a snippet of code that I'm using to extract the value (which doesn't work):
XElement root = XElement.Load(responseReader);
XNamespace tag = "tag:foo.com,2008:/my/data";
var status = (from s in root.Elements(tag + "Status")
select s).FirstOrDefault();
My status variable is always null. I've tried several variations on this, but to no avail. The part that's confusing me is the namespace -- tag and 2008 are defined. I don't know if I'm handling this correctly or if there's a better way to deal with this.
Also, I don't have control over the XML schema or the structure of the XML. The service I'm using is out of my control.
Thanks for any help!
Try Descendants() instead of Elements():
XElement x = XElement.Load(responseReader);
XNamespace ns = "tag:foo.com,2008:/my/data";
var status = x.Descendants(ns + "Status").FirstOrDefault().Value;
There are 2 Namespaces in the feed:
the Atom namespace
the tag namespace
The outer xml needs to use the Atom namespace, while a portion of the inner xml needs to use the tag namespace. i.e.,
var doc = XDocument.Load(responseReader);
XNamespace nsAtom = "http://www.w3.org/2005/Atom";
XNamespace nsTag = "tag:foo.com,2008:/my/data";
// get all entry nodes / use the atom namespace
var entry = doc.Root.Elements(nsAtom + "entry");
// get all StatusInfo elements / use the atom namespace
var statusInfo = entry.Descendants(nsTag + "StatusInfo");
// get all Status / use the tag namespace
var status = statusInfo.Elements(nsTag + "Status");
// get value of all Status
var values = status.Select(x => x.Value.ToString()).ToList();
I have an XML
<data>
<summary>
<account curr_desc='USD' acct_nbr='123' net='1000.00' />
<account curr_desc='USD' acct_nbr='456' net='2000.00' />
</summary>
<details>
<accounts>
<account acct_nbr="123" curr="USD">
<activity color='False' settle_date='02 Jul 2010' amt='580.00' />
<activity color='True' settle_date='09 Jul 2010' amt='420.00' />
</account>
<account acct_nbr="456" curr="USD">
<activity color='True' settle_date='12 Dec 2010' amt='1500.00' />
<activity color='True' settle_date='19 Dec 2010' amt='500.00' />
</account>
</accounts>
</details>
</data>
Using Linq and XDocument, I can extract "summary" information but how can I extract "account" information under "summary" tag?
XDocument XMLDoc = XDocument.Load("testdata.xml");
XElement accounts = (from xml2 in XMLDoc.Descendants("summary")
select xml2).FirstOrDefault();
How can I specify something like "summary/account" so that it returns me all the elements under <summary>? Note, that I have <account> under <detail><accounts>, I only want the elements under summary tag.
You should use the Elements method:
var accounts = doc.Root.Elements("summary").Elements("account");
Or, alternatively, XPathSelectElements, which in this case is simpler:
var accounts = doc.XPathSelectElements("/data/summary/account");
In this instance you can also use Descendants, as Andrew Barber suggested, but in general you should only do this when you really want to find all descendants with a given name, and not just immediate children. Otherwise your code does a lot of searching that it doesn't need to, and may return elements you don't want it to.
var accountSummaryElems =
XMLDoc.Element("summary").Elements("account");
This gives you a collection of the account elements under the summary element. You can then iterate them to get the values.
EDITED to use the same pattern you were; I call First() instead of FirstOrDefault() because that code won't run anyway if the "account" element is not found.
Then you have the right idea with iterating over the collection returned.
This returns child elements as a string list not matter where it is.
using System.Xml.Linq;
XDocument xmlDocument = XDocument.Load(fileName);
public List<string> FindChilds(string parentTag)
{
return xmlDocument.Descendants().Where(x => x.Parent != null).Where(x => x.Parent.Name.ToString().Equals(parentTag)).Select(x => x.Name.ToString()).ToList();
}