Linq2Xml - how to get nodes and childnodes related - c#

I have xml like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<supplyCrew xmlns="http://site.ddf.com">
<login>
<login>XXXX</login>
<password>XXXX</password>
</login>
<flightInformation>
<flights>
<item>
<arrivalDateTime>2010-11-08T22:48:00.000Z</arrivalDateTime>
<arrivingCity>ORD</arrivingCity>
<crewMembers>
<item>
<employeeId>020040</employeeId>
<isDepositor>Y</isDepositor>
<isTransmitter>N</isTransmitter>
</item>
<item>
<employeeId>09000</employeeId>
<isDepositor>N</isDepositor>
<isTransmitter>Y</isTransmitter>
</item>
</crewMembers>
</item>
<item>
<arrivalDateTime>2010-11-08T20:29:00.000Z</arrivalDateTime>
<arrivingCity>JFK</arrivingCity>
<crewMembers>
<item>
<employeeId>0538</employeeId>
<isDepositor>Y</isDepositor>
<isTransmitter>N</isTransmitter>
</item>
<item>
<employeeId>097790</employeeId>
<isDepositor>N</isDepositor>
<isTransmitter>Y</isTransmitter>
</item>
</crewMembers>
</item>
</flights>
</flightInformation>
</supplyCrew>
This code gets only the first item " and " and then generate a exception 'System.NullReferenceException'.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Xml;
using System.Data.SqlClient;
using System.Data;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
XElement doc = XElement.Load("U:/Crew.xml");
XNamespace e = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace s = "http://site.ddf.com";
var flights = doc.Elements(e + "Body")
.Elements(s + "supplyCrew")
.Elements(s + "flightInformation")
.Elements(s + "flights")
.Elements(s + "item")
.Select(
flight_item => new
{
//Anonymous Type
arrivalDateTime = flight_item.Element(s + "arrivalDateTime").Value,
arrivingCity = flight_item.Element(s + "arrivingCity").Value,
crewmember = flight_item.Elements(s + "crewMembers").Elements(s + "item"),
}
);
int index = 1;
foreach (var flight_item in flights)
{
Console.Write('\n'+"New flight item:"+'\n');
Console.Write('\t'+flight_item.arrivalDateTime + '\n');
Console.Write('\t'+flight_item.arrivingCity + '\n');
foreach (var item in flight_item.crewmember)
{
var employeeId = item;//crewmember.Elements(s + "item").Elements("employeeId");
Console.Write("\t "+employeeId);
Console.Write('\n');
//index++;
}
}
The idea is to get item.arrivalDateTime and item.arrivingCity and its crewMembers.item.employeeId for all the rows. However I do not know how to get the childnodes using linq...us occrring a exception if when try employeeId = x.Element(s + "employeeId").Value....
Result should be something like this:
2010-11-08T22:48:00.000Z; ORD; 020040
2010-11-08T22:48:00.000Z; ORD; 09000
2010-11-08T20:29:00.000Z; JFK; 0538
2010-11-08T20:29:00.000Z; JFK; 097790

The problem is in your use of Descendants. You're asking for all item descendants of flights elements - which means it will include this:
<item>
<employeeId>020040</employeeId>
<isDepositor>Y</isDepositor>
<isTransmitter>N</isTransmitter>
</item>
That clearly doesn't have an arrivalDateTime element, hence the problem.
Replacing every call of Descendants to Elements in your sample code fixes the problem... although you're still going to fail if the actual document doesn't have all of the expected data.

SOLVED
we just need to replace that foreach for :
foreach (var flight_item in l_flights)
{
Console.Write('\n'+"New flight item:"+'\n');
Console.Write('\t'+flight_item.arrivalDateTime + '\n');
Console.Write('\t'+flight_item.arrivingCity + '\n');
foreach (var item in flight_item.crewmember)
{
var employeeId = item.Element(s + "employeeId").Value;
var isDepositor = item.Element(s + "isDepositor").Value;
var isTransmitter = item.Element(s + "isTransmitter").Value;
Console.Write("\t " + employeeId + "\n");
Console.Write("\t " + isDepositor + "\n");
Console.Write("\t " + isTransmitter + "\n");
}
}
Thank you guys....

Related

c# parse soap response when namespace prefix and xmlns prefix not equal

Hello I'm completely stuck, I could manage somehow get response from remote service.svc wsdl. Sorry for poor English.
there is my working sample, thanks to stackoverflow:
using System;
using System.Xml;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string responseXml =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<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>" +
"<Company xmlns:b=\"http://www.someurl.com/Report/Company\">" +
"<b:LoginResult>" +
"<b:CookieName>FedAuth</b:CookieName>" +
"<b:ErrorCode>NoError</b:ErrorCode>" +
"<b:TimeoutSeconds>1800</b:TimeoutSeconds>" +
"<b:Parametrs>Zoroo 100</b:Parametrs>" +
"</b:LoginResult>" +
"</Company>" +
"</soap:Body>" +
"</Envelope>";
XmlDocument document = new XmlDocument();
document.LoadXml(responseXml); //loading soap message as string
XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
manager.AddNamespace("b", "http://www.someurl.com/Report/Company");
XmlNodeList xnList = document.SelectNodes("//b:LoginResult", manager);
int nodes = xnList.Count;
foreach (XmlNode xn in xnList)
{
string CookieName = xn["b:CookieName"].InnerText;
string ErrorCode = xn["b:ErrorCode"].InnerText;
string TimeoutSeconds = xn["b:TimeoutSeconds"].InnerText;
string Parametrs = xn["b:Parametrs"].InnerText;
Console.WriteLine();
Console.WriteLine(CookieName + ErrorCode + TimeoutSeconds + Parametrs);
Console.WriteLine();
}
}
}
}
My problem is that I cannot understand soap response message when I receive it like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GCRResponse xmlns="http://www.someurl.com/Report">
<GCRResult xmlns:a="http://www.someurl.com/CReport" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Company xmlns:b="http://www.someurl.com/Company">
<b:General>
<b:SomeInfo xmlns:c="http://www.someurl.com/Report/Common">
<c:Curr>BUM</c:Curr>
<c:LocalValue>1.00</c:LocalValue>
<c:Value>1.00</c:Value>
</b:SomeInfo>
<b:BS>Active</b:BS>
<b:CName>Zabis</b:CName>
<b:ES>NotSpecified</b:ES>
<b:NACELookup>
<a:NACE>NotSpecified</a:NACE>
</b:NACELookup>
<b:PC xmlns:c="http://www.someurl.com/Report/Common">
<c:Curr>BUM</c:Curr>
<c:LocalValue>1.00</c:LocalValue>
<c:Value>1.00</c:Value>
</b:PC>
</b:General>
</a:Company>
<a:CompanyRep i:nil="true" xmlns:b="http://www.someurl.com/Report/SomeReport"/>
<a:Parameters>
<a:Consent>true</a:Consent>
<a:IDNumber>30103331133</a:IDNumber>
<a:IDNumberType>RNumber</a:IDNumberType>
<a:InquiryReason>Application</a:InquiryReason>
<a:ReportDate>2017-09-26T08:57:26.6885118+03:00</a:ReportDate>
<a:Sections xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<b:string>SubjectInfo</b:string>
</a:Sections>
<a:SubjectType>Company</a:SubjectType>
</a:Parameters>
<a:ReportInfo xmlns:b="http://www.someurl.com/Report/ReportInfo">
<b:Created>2017-09-26T08:57:26.7197686+03:00</b:Created>
<b:ReferenceNumber>333368-9543457</b:ReferenceNumber>
<b:ReportStatus>ReportGenerated</b:ReportStatus>
<b:RequestedBy i:nil="true"/>
<b:Subscriber i:nil="true"/>
<b:Version>325</b:Version>
</a:ReportInfo>
</GCRResult>
</GCRResponse>
</s:Body>
</s:Envelope>
how to access to values if I have namespace with different prefix (a,b) than xmlns prefix for example here
<a:Company xmlns:b="http://www.someurl.com/Company">
and I cannot figure out how to work with manager.AddNamespace in this case when a:Company and is xmlns:b
How to access all values a,b,c?

How to add a ':' char to an XML attribute

How do I add the char : to an XML element attribute?
Here is how the output should look like
<alto xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.loc.gov/standards/alto/ns-v3#"
xsi:schemaLocation="http://www.loc.gov/standards/alto/ns-v3# http://www.loc.gov/standards/alto/v3/alto-3-1.xsd" SCHEMAVERSION="3.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
and so far this is my code
var z = doc.Descendants("alto").First();
XNamespace ns = "http://www.w3.org/2001/XMLSchema-instance";
z.Add(new XAttribute(XNamespace.Xmlns + "xsi", ns.NamespaceName));
I tried this code, but it gives me error
new XAttribute("xmlns", "http://www.loc.gov/standards/alto/ns-v3#")
and here is the error message:
The prefix '' cannot be redefined from '' to 'http://www.loc.gov/standards/alto/ns-v3#' within the same start element tag
You should put default namespace last. For complex namespaces like yours I usually just parse the string like code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<alto xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://www.loc.gov/standards/alto/ns-v3#\"" +
" xsi:schemaLocation=\"http://www.loc.gov/standards/alto/ns-v3# http://www.loc.gov/standards/alto/v3/alto-3-1.xsd\" SCHEMAVERSION=\"3.1\"" +
" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" +
"</alto>";
XDocument doc = XDocument.Parse(xml);
XElement root = doc.Root;
XNamespace ns = root.GetDefaultNamespace();
XNamespace nsXsi = root.GetNamespaceOfPrefix("xsi");
XNamespace nsSchemaLocation = root.GetNamespaceOfPrefix("schemaLocation");
XNamespace nsXlink = root.GetNamespaceOfPrefix("xlink");
}
}
}

Get Innertext from an XML string which has only one node without looping

I have an XML string which looks like below
<?xml version="1.0" encoding="Windows-1252"?><Product><ID>0701161476416</ID><UNIQUE_ID>test26051602</UNIQUE_ID><STATUS>DONE</STATUS></Product>
It is know that my XML string will always has a single node ans so I do not want to look, instead I would like to get Unique_ID and Status inner values without looping.
May I know a better way to do it and I do have the below code which actually loops through each node
XmlDocument xm = new XmlDocument();
xm.LoadXml(XML_STRING);
XmlNodeList xnList = xm.SelectNodes("/Product/Product");
foreach (XmlNode xn in xnList)
{
string uniqueID = xn["UNIQUE_ID"].InnerText;
string status = xn["STATUS"].InnerText;
}
There is SelectSingleNode() which you can use for this purpose :
XmlNode product = xm.SelectSingleNode("/Product/Product");
string uniqueID = product["UNIQUE_ID"].InnerText;
string status = product["STATUS"].InnerText;
Or, if Product is the root element, then you can access it from DocumentElement property of the XmlDocument :
XmlNode product = xm.DocumentElement;
string uniqueID = product["UNIQUE_ID"].InnerText;
string status = product["STATUS"].InnerText;
If you have more than one product try 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
{
static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\" encoding=\"Windows-1252\"?>" +
"<Products>" +
"<Product>" +
"<ID>0701161476416</ID>" +
"<UNIQUE_ID>test26051603</UNIQUE_ID>" +
"<STATUS>DONE</STATUS>" +
"</Product>" +
"<Product>" +
"<ID>0701161476417</ID>" +
"<UNIQUE_ID>test26051604</UNIQUE_ID>" +
"<STATUS>DONE</STATUS>" +
"</Product>" +
"<Product>" +
"<ID>0701161476418</ID>" +
"<UNIQUE_ID>test26051605</UNIQUE_ID>" +
"<STATUS>DONE</STATUS>" +
"</Product>" +
"</Products>";
XDocument doc = XDocument.Parse(xml);
var results = doc.Descendants("Product").Select(x => new
{
id = (long)x.Element("ID"),
uniqueID = (string)x.Element("UNIQUE_ID"),
status = (string)x.Element("STATUS")
}).ToList();
}
}
}
This could work, but I guess there is a better solution:
XDocument xm = XDocument.Parse(XML_STRING);
var product = xm.Element("Product").Element("Product");
string uniqueID = product.Element("UNIQUE_ID").Value;
string status = product.Element("STATUS").Value;
Your SelectNodes line seemed wrong for the sample Xml.
XmlNodeList xnList = xm.SelectNodes("/Product");
if (xnList.Count > 0)
{
string uniqueID = xnList[0]["UNIQUE_ID"].InnerText;
string status = xnList[0]["STATUS"].InnerText;
}

XML.Linq - get value based on another value

I am trying to user Linq to XML to pull out a value from some xml based on another value..
Here is my xml
<Lead>
<ID>1189226</ID>
<Client>
<ID>8445254</ID>
<Name>City of Lincoln Council</Name>
</Client>
<Contact>
<ID>5747449</ID>
<Name>Fred Bloggs</Name>
</Contact>
<Owner>
<ID>36612</ID>
<Name>Joe Bloggs</Name>
</Owner>
<CustomFields>
<CustomField>
<ID>31961</ID>
<Name>Scotsm_A_n_Authority_Good</Name>
<Boolean>true</Boolean>
</CustomField>
<CustomField>
<ID>31963</ID>
<Name>Scotsma_N_Need Not Want</Name>
<Boolean>false</Boolean>
</CustomField>
</CustomFields>
So, for example, I want to try and get the value of <Boolean> in the <CustomField> where <Name> equals "Scotsm_A_n_Authority_Good" which should be "true"
I have tried the following:
var xmlAnswers = xe.Element("CustomFields").Element("CustomField").Element("Scotsm_A_n_Authority_Good");
But I get an error saying that:
The ' ' character, hexadecimal value 0x20, cannot be included in a name...
Can anyone help please?
You're looking for the wrong thing at the moment. You should be looking for a CustomField element with a Name element with the specified value:
string target = "Scotsm_A_n_Authority_Good"; // Or whatever
var xmlAnswers = xe.Element("CustomFields")
.Elements("CustomField")
.Where(cf => (string) cf.Element("Name") == target);
This will give you all the matching CustomField elements. You can then get the Boolean value with something like:
foreach (var answer in xmlAnswers)
{
int id = (int) answer.Element("ID");
bool value = (bool) answer.Element("Boolean");
// Use them...
}
(You could extract the ID and value in the LINQ query of course...)
Here is an xml linq solution
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication93
{
class Program
{
static void Main(string[] args)
{
string xml =
"<Lead>" +
"<ID>1189226</ID>" +
"<Client>" +
"<ID>8445254</ID>" +
"<Name>City of Lincoln Council</Name>" +
"</Client>" +
"<Contact>" +
"<ID>5747449</ID>" +
"<Name>Fred Bloggs</Name>" +
"</Contact>" +
"<Owner>" +
"<ID>36612</ID>" +
"<Name>Joe Bloggs</Name>" +
"</Owner>" +
"<CustomFields>" +
"<CustomField>" +
"<ID>31961</ID>" +
"<Name>Scotsm_A_n_Authority_Good</Name>" +
"<Boolean>true</Boolean>" +
"</CustomField>" +
"<CustomField>" +
"<ID>31963</ID>" +
"<Name>Scotsma_N_Need Not Want</Name>" +
"<Boolean>false</Boolean>" +
"</CustomField>" +
"</CustomFields>" +
"</Lead>";
XDocument doc = XDocument.Parse(xml);
//to get all customField
var results = doc.Descendants("CustomField").Select(x => new
{
id = (int)x.Element("ID"),
name = (string)x.Element("Name"),
boolean = (Boolean)x.Element("Boolean")
}).ToList();
//to get specific
XElement Scotsm_A_n_Authority_Good = doc.Descendants("CustomField")
.Where(x => ((string)x.Element("Name") == "Scotsm_A_n_Authority_Good") && (Boolean)(x.Element("Boolean"))).FirstOrDefault();
}
}
}

Merge 2 XML Files Into Same Elements C#

I need to create a WebService that takes the name of a city as input and returns Location, Country and Weather information of the City. The thing is that the ID, Location, Country is in one XML file and all weather details in another.
<City>
<ID>city1</ID>
<Grid_ref>NG 895608</Grid_ref>
<Name>Berlin</Name>
<Country>Germany</Country>
</City>
<cityWeather>
<ID>city1</ID>
<temperature>20</temperature>
<wind>2</wind>
</cityWeather>
Using c# is it possible to merge everything into 1 file using ID or is there some other way I can do it? I would then search the XML file once, as having 2 different files mixes me up.
You can use DataSet. I suppose you have two XML files. CityWeather.xml et City.xml, you can make this
try
{
XmlTextReader xmlreader1 = new XmlTextReader("C:\\Books1.xml");
XmlTextReader xmlreader2 = new XmlTextReader("C:\\Books2.xml");
DataSet ds = new DataSet();
ds.ReadXml(xmlreader1);
DataSet ds2 = new DataSet();
ds2.ReadXml(xmlreader2);
ds.Merge(ds2);
ds.WriteXml("C:\\Books.xml");
Console.WriteLine("Completed merging XML documents");
}
catch (System.Exception ex)
{
Console.Write(ex.Message);
}
Console.Read();
You can make any changes that meet your need
hope it helps
Use add
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string cityXML =
"<Root>" +
"<City>" +
"<ID>city1</ID>" +
"<Grid_ref>NG 895608</Grid_ref>" +
"<Name>Berlin</Name>" +
"<Country>Germany</Country>" +
"</City>" +
"<City>" +
"<ID>city2</ID>" +
"<Grid_ref>F 5608</Grid_ref>" +
"<Name>Paris</Name>" +
"<Country>France</Country>" +
"</City>" +
"<City>" +
"<ID>city3</ID>" +
"<Grid_ref>RR 608</Grid_ref>" +
"<Name>Rome</Name>" +
"<Country>Italy</Country>" +
"</City>" +
"</Root>";
XElement cities = XElement.Parse(cityXML);
string weatherXML =
"<Root>" +
"<cityWeather>" +
"<ID>city1</ID>" +
"<temperature>20</temperature>" +
"<wind>2</wind>" +
"</cityWeather>" +
"<cityWeather>" +
"<ID>city2</ID>" +
"<temperature>30</temperature>" +
"<wind>3</wind>" +
"</cityWeather>" +
"<cityWeather>" +
"<ID>city3</ID>" +
"<temperature>40</temperature>" +
"<wind>4</wind>" +
"</cityWeather>" +
"</Root>";
XElement weather = XElement.Parse(weatherXML);
List<XElement> cityList = cities.Descendants("City").ToList();
foreach(XElement city in cityList)
{
XElement matchedCity = weather.Descendants("cityWeather").Where(x =>
x.Element("ID").Value == city.Element("ID").Value).FirstOrDefault();
if(matchedCity != null) city.Add(matchedCity);
}
}
}
}
​

Categories

Resources