c# Xdocument and descendants with the same name not working - c#

Hi have the following XML with the same Transaction name (can't change it because this is how it comes from the origin):
<StoreCenter Operation="update" xmlns="http://something.com/rdc.xsd">
<Transaction>
<Transaction>
<StoreID>30</StoreID>
<TransactionID>2</TransactionID>
<RegisterTransNumber>2</RegisterTransNumber>
....
</Transaction>
<Transaction>
<StoreID>30</StoreID>
<TransactionID>3</TransactionID>
<RegisterTransNumber>2</RegisterTransNumber>
....
<Transaction>
<Transaction>
<StoreCenter>
I have the following code and I'm new in LINQ, I'm trying to retrive the StoreID for each Transaction Child:
XDocument Doc = XDocument.Load(filename);
XNamespace ns = "http://something.com/rdc.xsd";
foreach (var StoreCenter in Doc.Descendants(ns + "StoreCenter"))
{
foreach (var Transaction in StoreCenter.Descendants("Transaction"))
{
foreach (var TransactionCh in Transaction.Descendants("Transaction"))
{
Console.WriteLine(Transaction.Element("StoreID").Value);
}
}
}
But I'm getting nothing, what am I doing wrong?, is this a good approach to retrieve these values?, please your advice will be appreciated

Remove the curly brace from here:
Doc.Descendants("{" + ns + "StoreCenter")
^^^
And you should specify the namespace with child elements as well.Like: StoreCenter.Descendants(ns + "Transaction") and Transaction.Descendants(ns + "Transaction")
if you just want to get Transaction elements you can just to : Doc.Descendants(ns + "Transaction") or Doc.Root.Element(ns + "Transaction").Elements(ns + "Transaction"); (assuming StoreCenter is the root element)

If your XML is correctly closed (i assume that the Transaction and StoreCenter nodes from the example XML are closed), then - yes, your approach using LINQ2XML is correct.
You could enhance the code using the XElement.GetDefaultNamespace() method to retrieve the default namespace easily (easier than typing). A solution could look like this:
var xml = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<StoreCenter Operation=""update"" xmlns=""http://something.com/rdc.xsd"">
<Transaction>
<Transaction>
<StoreID>30</StoreID>
<TransactionID>2</TransactionID>
<RegisterTransNumber>2</RegisterTransNumber>
</Transaction>
<Transaction>
<StoreID>30</StoreID>
<TransactionID>3</TransactionID>
<RegisterTransNumber>2</RegisterTransNumber>
</Transaction>
</Transaction>
</StoreCenter>";
var xmlDocument = XDocument.Parse(xml); // or XDocument.Load(xml);
var ns = xmlDocument.Root.GetDefaultNamespace();
var transactions = xmlDocument
.Root // the StoreCenter root node
.Element(ns + "Transaction") // the enclosing Transaction node
.Elements(ns + "Transaction") // only Transaction subnodes
.ToList();
foreach (var transaction in transactions)
{
var storeId = transaction.Element(ns + "StoreID").Value;
Console.WriteLine(storeId);
}
And the output is:
30
30

Related

Load data from XML nodes

i have this structure of data:
<?xml version="1.0" encoding="windows-1250"?>
<?xml-stylesheet type="text/xsl" href="usb71105.xsl"?>
<manas:usb xmlns:manas="http://www.manas.info/">
<manas:qr00>
<manas:verzemanas>26052708</manas:verzemanas>
<manas:verzexml>2016.03.29a</manas:verzexml>
<manas:druhtisku>U_Tisk2P/2159405/TRUE</manas:druhtisku>
</manas:qr00>
<manas:qr00>
<manas:verzemanas>26052710</manas:verzemanas>
<manas:verzexml>2016.03.30a</manas:verzexml>
<manas:druhtisku>U_Tisk2P/FALSE</manas:druhtisku>
</manas:qr00>
</manas:usb>
I need to save values of: manas:verzemanas ; manas:verzexml ;
I have this code:
XmlDocument doc = new XmlDocument();
doc.Load("d:\\83116623.XML");
foreach (XmlNode node in doc.DocumentElement)
{
string name = node.Attributes[0].ToString();
}
Have you any ideas please?
You're probably better off with XDocument. Also you need to use the namespace prefix. E.g.:
XNamespace ns = "http://www.manas.info/";
var xdoc = XDocument.Load(#"c:\temp\a\a.xml");
var verze = xdoc.Root.Elements(ns + "qr00")
.Elements(ns + "verzemanas")
.Select(e => e.Value);
verze.ToList().ForEach(v => Console.WriteLine(v));
prints
26052708
26052710

How to update an XML node with LINQ?

How do we update the a node name with a new value using LINQ?
<test xmlns="http://www.mydomain.com/test/xyz" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<Ribbon1>test</Ribbon1>
<Ribbon2>test</Ribbon2>
</test>
I was trying as below:
var query = from lst in XElement.Load(fileLoc).Elements(ns + "Ribbon1")
select lst.FirstNode ;
The below code is working now:
XNamespace ns = #"http://www.mydomain.com/test/xyz";
XElement xElement = XElement.Load(fileLoc);
foreach (XElement descendant in xElement.Descendants(ns + "Ribbon1"))
descendant.Value = "Borra";
xElement.Save(fileLoc);
Your code use Elemnts and that only looks at the given level. To find something at an arbitrary level:
//XElement.Load(fileLoc).Elements(ns + "Ribbon1")
XElement.Load(fileLoc).Descendants(ns + "Ribbon1")
or to adhere to the structure:
XElement.Load(fileLoc).Element(ns + "test").Elements(ns + "Ribbon1")
be careful about Element() and Elements()
XNamespace ns = #"http://www.mydomain.com/test/xyz";
XElement xElement = XElement.Load(fileLoc);
foreach (XElement descendant in xElement.Descendants(ns + "Ribbon1"))
descendant.Value = "Borra";
xElement.Save(fileLoc);

Get specific values from Xml

I don't how to extract the values from XML document, and am looking for some help as I'm new to C#
I am using XmlDocument and then XmlNodeList for fetching the particular XML document
Here is my code
XmlNodeList XMLList = doc.SelectNodes("/response/result/doc");
And my XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<result>
<doc>
<long name="LoadID">494</long>
<long name="EventID">5557</long>
<str name="XMLData"><TransactionDate>2014-05-28T14:17:31.2186777-06:00</TransactionDate><SubType>tblQM2222Components</SubType><IntegerValue title="ComponentID">11111</IntegerValue></str></doc>
<doc>
<long name="LoadID">774</long>
<long name="EventID">5558</long>
<str name="XMLData"><TransactionDate>2014-05-28T14:17:31.2186777-06:00</TransactionDate><SubType>tblQM2222Components</SubType><IntegerValue title="ComponentID">11111</IntegerValue></str></doc>
</result>
</response>
In this i have to fetch every the XMLData data that is under every doc tag and i have to fetch last doc tag EventID.
var xml = XDocument.Parse(xmlString);
var docs = xml.Root.Elements("doc");
var lastDocEventID = docs.Last()
.Elements("long")
.First(l => (string)l.Attribute("name") == "EventID")
.Value;
Console.WriteLine ("Last doc EventId: " +lastDocEventID);
foreach (var doc in docs)
{
Console.WriteLine (doc.Element("str").Element("TransactionDate").Value);
}
prints:
Last doc EventId: 5558
2014-05-28T14:17:31.2186777-06:00
2014-05-28T14:17:31.2186777-06:00
You can use two XPath expressions to select the nodes you want. To answer each part of your question in turn:
To select all of the XMLData nodes:
XmlNodeList XMLList
= doc.SelectNodes("/response/result/doc/str[#name='XMLData']");
To select the last EventId:
XmlNode lastEventIdNode =
doc.SelectSingleNode("/response/result/doc[position() =
last()]/long[#name='EventID']");
If not all doc nodes are guaranteed to have an event id child node, then you can simply:
XmlNodeList eventIdNodes =
doc.SelectNodes("/response/result/doc[long[#name='EventID']]");
XmlNode lastNode = eventIdNodes[eventIdNodes.Count - 1];
That should give you what you've asked for.
Update;
If you want the XML data inside each strXml element, you can use the InnerXml property:
XmlNodeList xmlList
= doc.SelectNodes("/response/result/doc/str[#name='XMLData']");
foreach(XmlNode xmlStrNode in xmlList)
{
string xmlInner = xmlStrNode.InnerXml;
}
There's one result tag short in your xml.
Try using this. It's cleaner too imho
XmlNodeList docs = doc.SelectSingleNode("response").SelectSingleNode("result").SelectNodes("doc");
Then you can use a combination of SelectSingleNode, InnerText, Value to get the data from each XmlNode in your list.
For example if you want the EventID from the first doc tag:
int eventID = int.Parse(docs[0].SelectSingleNode("long[#name='EventID']").InnerText);

Access all nodes in xml using linq

I have xml:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<UpdateMemberHireStatus xmlns="http://tempuri.org/">
<member>
<HireAvailability>
<code>1</code>
<name>פנוי</name>
</HireAvailability>
<HireRejectReason>
<code>2</code>
<name>wow</name>
</HireRejectReason>
<IdNumber>43504349</IdNumber>
<note> </note>
</member>
</UpdateMemberHireStatus>
and I want to use LINQ to access all the nodes in the xml.
Here's what I have tried:
XNamespace ns = "tempuri.org/";
IEnumerable<HireStatus> status = from r in doc.Descendants(ns + "UpdateMemberHireStatus")
.Descendants(ns + "member")
select new HireStatus() { };
return status.ToList();
Use Descendants
var xml = XDocument.Load(XMLStream);
var allEle = xml.Descendants("UpdateMemberHireStatus"); //you can do linq now.
You can use XDocument also in the following way:
string xmlPath = "D://member.xml";
XmlDocument doc = new XmlDocument();
xdoc = XDocument.Load(xmlPath);
doc.LoadXml(xdoc.ToString());
var memberStatus= (from mem in xdoc.Descendants("member")
select mem);
foreach (XElement element in memberStatuses.ToList())
IEnumerable<XNode> nodes = element.Nodes();
var x = XElement.Load(XMLStream);
var all = x.DescendantNodes();

XDocument and Linq returns null if the element has xmlns attribute

Newbie with XDocuments and Linq, please suggest a solution to retrieve the data from a particular tag in the xml string:
If I have a XML string from webservice response (I formatted xml for ease):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetCashFlowReportResponse xmlns="http://tempuri.org/">
<GetCashFlowReportPdf>Hello!</GetCashFlowReportPdf>
</GetCashFlowReportResponse>
</soap:Body>
</soap:Envelope>
Using the following code, I can get the value only if GetCashFlowReportResponse tag doesn't have "xmlns" attribute. Not sure why? Otherwise, it always return null.
string inputString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><soap:Body><GetCashFlowReportResponse xmlns=\"http://tempuri.org/\"><GetCashFlowReportPdf>Hello!</GetCashFlowReportPdf></GetCashFlowReportResponse></soap:Body></soap:Envelope>"
XDocument xDoc = XDocument.Parse(inputString);
//XNamespace ns = "http://tempuri.org/";
XNamespace ns = XNamespace.Get("http://tempuri.org/");
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element("GetCashFlowReportPdf");
foreach (string val in data)
{
Console.WriteLine(val);
}
I can't change the web service to remove that attribute. IS there a better way to read the response and get the actual data back to the user (in more readable form)?
Edited:
SOLUTION:
XDocument xDoc = XDocument.Parse(inputString);
XNamespace ns = "http://tempuri.org/";
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element(ns + "GetCashFlowReportPdf");
foreach (string val in data)
{
Console.WriteLine(val);
}
Note: Even if all the child elements doesn't have the namespace attribute, the code will work if you add the "ns" to the element as I guess childs inherit the namespace from parent (see response from SLaks).
You need to include the namespace:
XNamespace ns = "http://tempuri.org/";
xDoc.Descendants(ns + "GetCashFlowReportResponse")
XName qualifiedName = XName.Get("GetCashFlowReportResponse",
"http://tempuri.org/");
var data = from d in xDoc.Descendants(qualifiedName)
Just ask for the elements using the qualified names:
// create a XML namespace object
XNamespace ns = XNamespace.Get("http://tempuri.org/");
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element(ns + "GetCashFlowReportPdf");
Note the use of the overloaded + operator that creates a QName with a XML namespace and a local name string.

Categories

Resources