Getting attributes from xml using Linq - c#

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

Related

Reading data from XML with attributes

I have an XML file which is like below:
<CPageDataXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<control id="busRowOAppr2EIDLookUpUserControl" controltype="business">
<field controlvaluetype="single" key="busRowOAppr2EIDLookUpUserControl_txtEID">
<valuefield value="709227">E8 - John Doe</valuefield>
</field>
<field controlvaluetype="hidden_single" key="busRowOAppr2EIDLookUpUserControl_txtEID_Email">
<valuefield value="_JohnDoe#Wonder.com">emailid</valuefield>
</field>
</control>
<control id="busDelegationFromDate123" controltype="business">
<field controlvaluetype="single" key="txtCalanderDateWithImage_UserControl">
<valuefield value="" />
</field>
</control>
</CPageDataXML>
I want to read the value of the valuefield where control id="busRowOAppr2EIDLookUpUserControl"
The C# code is:
This is the code for loading the XML:
XmlDocument xPagedata=new XmlDocument();
XmlNode xnodePagedata = null;
xPagedata.LoadXml(strPageData);
This is the code for SelectSingleNode:
string a = xnodePagedata.SelectSingleNode(//Control[#id='busRowOAppr2EIDLookUpUserControl']).Attributes["Value"].Value;
I have tried to use SelectSingleNode(string) but that is giving me a null reference exception. Kindly suggest how should I go about this one. I am an absolute beginer on XML.
One possible way using the same approach :
string a =
xnodePagedata.SelectSingleNode("//control[#id='busRowOAppr2EIDLookUpUserControl']/field/valuefield/#value")
.Value;
UPDATE :
In case there are multiple <valuefield> in one <control> and you want all values, use SelectNodes() for example :
var values =
xPagedata.SelectNodes("//control[#id='busRowOAppr2EIDLookUpUserControl']/field/valuefield/#value");
foreach (XmlNode value in values)
{
Console.WriteLine(value.Value);
}
You can use XDocument : use Descendants("control") to get all controls then filter them using the Where clause then use SelectMany to get a flattened collection of values of valuefield.
XDocument doc = XDocument.Load(filepath);
var result = doc.Descendants("control")
.Where(i => (string)i.Attribute("id") == "busRowOAppr2EIDLookUpUserControl")
.SelectMany(i => i.Descendants("valuefield")
.Select(j => j.Attribute("value")))
.ToList();
And this is the result:
result Count = 2
[0] {value="709227"}
[1] {value="_JohnDoe#Wonder.com"}

Extract a node from xml response

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
}

Inconsistent XPATH query results

I'm in the process of running a conversion process to update some of our xml files. Here's a sample file:
<?xml version="1.0" encoding="utf-8"?>
<Jobs xmlns="urn:mynamespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Job name="Job1">
<Category>Maintenance</Category>
<Description>Purge records</Description>
<Steps>
<TSql name="Start Job" database="IADS">
<CommandText>Exec StoredProcedureName</CommandText>
<OnSuccess action="GotoNextStep" />
<Retries>0</Retries>
<OnFailure action="QuitFailure" />
<OutputFile />
</TSql>
<TSql name="Start LoadRequestManagementReportTables" database="msdb">
<CommandText>exec sp_start_job #job_name = 'Load Request Management Report Tables'</CommandText>
<OnSuccess action="QuitSuccess" />
<Retries waitInMinutes="0">0</Retries>
<OnFailure action="QuitFailure" />
<OutputFile />
</TSql>
</Steps>
<Schedules>
<Schedule>
<Weekly name="Every Sunday at 7:00 AM" enabled="false">
<BeginDate>2008-11-01</BeginDate>
<RunTimes>
<Once>07:00</Once>
</RunTimes>
<DaysOfWeek>Sunday</DaysOfWeek>
</Weekly>
</Schedule>
<Schedule>
<OneTime name="OneTime" enabled="false" rundate="2011-01-12T03:00:00" />
</Schedule>
</Schedules>
<Notifications>
<EventLog on="Failure" />
</Notifications>
</Job>
</Jobs>
Now, I'm trying to rename the first step in each of the files that I process to convert it from a TSql step to a CmdExec step. Therefore, I wrote my own "RenameNode" method to do this conversion and copy all attributes and nodes to the newly-named node.
Here's the RenameNode method:
private static void RenameNode(XmlNode node, string namespaceURI, string newName)
{
if (node.NodeType != XmlNodeType.Element)
return;
XmlElement oldElement = (XmlElement)node;
XmlElement newElement = node.OwnerDocument.CreateElement(newName, namespaceURI);
while (oldElement.HasAttributes)
newElement.SetAttributeNode(oldElement.RemoveAttributeNode(oldElement.Attributes[0]));
while (oldElement.HasChildNodes)
newElement.AppendChild(oldElement.FirstChild);
if (oldElement.ParentNode != null)
oldElement.ParentNode.ReplaceChild(newElement, oldElement);
}
The problem I'm having is that the second query I run is not returning results.
Here's query #1 that I run:
XmlNodeList stepNodes = xSchedule.SelectNodes("/mns:Jobs/mns:Job/mns:Steps/mns:TSql", nsm);
This works great, returns 2 nodes in my stepNodes variable. I process the "RenameNode" method for stepNodes[0]. Fantastic.
The next thing I want to do is remove the namespace attributes that the "RenameNode" method generates (I'm guessing ya'll will not like that....but that's not the issue). So, in order to do that, I try running a very similar XPATH query that looks like this, but it does NOT return any records:
stepNodes = xSchedule.SelectNodes("/mns:Jobs/mns:Job/mns:Steps/mns:CmdExec", nsm);
I've tried resetting the namespace manager, I've tried saving the xml file (xSchedule.Save()), tried running the query with and without the namespace prefixes/parameter, etc. It NEVER returns any nodes. Even after doing the rename to the node, after saving the document I can see that it successfully renamed that node (and thus added the xmlns attribute to EVERYTHING in that newly-named node). I even verified that the original query that returns results now returns only 1 node this time.
// Let's assume that the very first node is the node that we want to change
XmlNodeList stepNodes = xSchedule.SelectNodes("/mvst:Jobs/mvst:Job/mvst:Steps/mvst:TSql", nsm);
if (stepNodes.Count >= 1)
{
RenameNode(stepNodes[0], String.Empty, "CmdExec");
// After renaming the node, let's remove the "database" attribute if it exists
//XmlElement e = (XmlElement)stepNodes[0];
//e.RemoveAttribute("database");
xSchedule.Save(scheduleXmlFile + ".bak");
}
Where am I going wrong with this code?
Just need to pass the namespace information to the RenameNode method.
RenameNode(stepNodes[0], "urn:mynamespace", "CmdExec");

Load string to LINQ classes and query it

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

Using Linq and XDocument, can I get all the child elements under parent tag?

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();
}

Categories

Resources