how can i get node Element value From LINQ? - c#

i have a collection of service users, i want to iterate over ServiceUsers and extract value from ServiceUser, (ID, USER_NAME, UN_ID, IP, NAME)
<ServiceUsers xmlns="">
<ServiceUser>
<ID>280334</ID>
<USER_NAME>YVELAMGAMOIYENET12:206322102</USER_NAME>
<UN_ID>731937</UN_ID>
<IP>91.151.136.178</IP>
<NAME>?????????????????????: 123456</NAME>
</ServiceUser>
<ServiceUser>
<ID>266070</ID>
<USER_NAME>ACHIBALANCE:206322102</USER_NAME>
<UN_ID>731937</UN_ID>
<IP>185.139.56.37</IP>
<NAME>123456</NAME>
</ServiceUser>
</ServiceUsers>
my Code looks like this, but i am getting null point exception.
XDocument doc = XDocument.Parse(xml)
List<XElement> xElementList = doc.Element("ServiceUsers").Descendants().ToList();
foreach (XElement element in xElementList)
{
string TEST= element.Element("Name").Value;
comboBoxServiceUser.Items.Add(element.Element("Name").Value);
}

I used the example from XmlSerializer.Deserialize Method as the base for the following snippet that reads the provided xml.
var serializer = new XmlSerializer(typeof(ServiceUsers));
ServiceUsers i;
using (TextReader reader = new StringReader(xml))
{
i = (ServiceUsers)serializer.Deserialize(reader);
}
[XmlRoot(ElementName = "ServiceUsers")]
public class ServiceUsers : List<ServiceUser>
{
}
public class ServiceUser
{
[XmlElement(ElementName = "ID")]
public string Id {get; set;}
}

I think the basis of the problem is your trailing 's' to put it short, You iterate ServiceUser not ServiceUsers
Anyway this runs through fine:
[Fact]
public void CheckIteratorTest()
{
var a = Assembly.GetExecutingAssembly();
string[] resourceNames = a.GetManifestResourceNames();
string nameOf = resourceNames.FirstOrDefault(x => x.Contains("SomeXml"));
Assert.NotNull(nameOf);
using var stream = a.GetManifestResourceStream(nameOf);
Assert.NotNull(stream);
var reader = new StreamReader(stream, Encoding.UTF8);
var serialized = reader.ReadToEnd();
var doc = XDocument.Parse(serialized);
var elemList = doc.Root.Elements("ServiceUser").ToList();
Assert.NotEqual(0, elemList.Count);
foreach(var serviceUser in elemList)
{
System.Diagnostics.Debug.WriteLine($"Name : {serviceUser.Name ?? "n.a."}");
}
}

As being said: XML is case-sensitive. Next issue is .Descendants() returns all the descendant nodes, nested ones, etc, 12 nodes in this case. So NullPointerException will happen even if you fix a "typo".
Here is your fixed code:
XDocument doc = XDocument.Parse(xml);
var xElementList = doc
.Element("ServiceUsers") // picking needed root node from document
.Elements("ServiceUser") // picking exact ServiceUser nodes
.Elements("NAME") // picking actual NAME nodes
.ToList();
foreach (XElement element in xElementList)
{
var TEST = element.Value;
Console.WriteLine(TEST); // do what you were doing instead of console
}

Use doc.Element("ServiceUsers").Elements() to get the<ServiceUser> elements. Then you can loop over the child values of those in a nested loop.
var doc = XDocument.Parse(xml);
foreach (XElement serviceUser in doc.Element("ServiceUsers").Elements()) {
foreach (XElement element in serviceUser.Elements()) {
Console.WriteLine($"{element.Name} = {element.Value}");
}
Console.WriteLine("---");
}
Prints:
ID = 280334
USER_NAME = YVELAMGAMOIYENET12:206322102
UN_ID = 731937
IP = 91.151.136.178
NAME = ?????????????????????: 123456
---
ID = 266070
USER_NAME = ACHIBALANCE:206322102
UN_ID = 731937
IP = 185.139.56.37
NAME = 123456
---
Note: Elements() gets the (immediate) child elements where as Descendants() returns all descendants. Using Elements() gives you a better control and allows you to get the properties grouped by user.
You can also get a specific property like this serviceUser.Element("USER_NAME").Value. Note that the tag names are case sensitive!

Related

how to get value from xml by Linq

i was reading huge xml file of 5GB size by using the following code, and i was success to get the first element Testid but failed to get another element TestMin coming under different namespace
this is the xml i am having
which i am getting as null
.What is wrong here?
EDIT
GMileys answer giving error like The ':' character, hexadecimal value 0x3A, cannot be included in a name
The element es:qRxLevMin is a child element of xn:attributes, but it looks like you are trying to select it as a child of xn:vsDataContainer, it is a grandchild of that element. You could try changing the following:
var dataqrxlevmin = from atts in pin.ElementsAfterSelf(xn + "VsDataContainer")
select new
{
qrxlevmin = (string)atts.Element(es + "qRxLevMin"),
};
To this:
var dataqrxlevmin = from atts in pin.Elements(string.Format("{0}VsDataContainer/{1}attributes", xn, es))
select new
{
qrxlevmin = (string)atts.Element(es + "qRxLevMin"),
};
Note: I changed your string concatenation to use string.Format for readability purposes, either is technically fine to use, but string.Format is a better approach.
What about this approach?
XDocument doc = XDocument.Load(path);
XName utranCellName = XName.Get("UtranCell", "un");
XName qRxLevMinName = XName.Get("qRxLevMin", "es");
var cells = doc.Descendants(utranCellName);
foreach (var cell in cells)
{
string qRxLevMin = cell.Descendants(qRxLevMinName).FirstOrDefault();
// Do something with the value
}
try this code which is very similar to your code but simpler.
using (XmlReader xr = XmlReader.Create(path))
{
xr.MoveToContent();
XNamespace un = xr.LookupNamespace("un");
XNamespace xn = xr.LookupNamespace("xn");
XNamespace es = xr.LookupNamespace("es");
while (!xr.EOF)
{
if(xr.LocalName != "UtranCell")
{
xr.ReadToFollowing("UtranCell", un.NamespaceName);
}
if(!xr.EOF)
{
XElement utranCell = (XElement)XElement.ReadFrom(xr);
}
}
}
actually namespace was the culprit,what i did is first loaded the small section i am getting from.Readform method in to xdocument,then i removed all the namespace,then i took the value .simple :)

How to not deserialize contents of node

Say I have the following xml:
<Samples>
<Sample>
<SomeStuff>
<SomMoreStuff>.. </SomeMoreStuff>
</SomeStuff>
</Sample>
<Sample>
<SomeStuff>
<SomMoreStuff>.. </SomeMoreStuff>
</SomeStuff>
</Sample>
</Samples>
How can I deserilaize this but have all text inside of < Sample > remain as a string? I dont want to parse the contents of Sample
[XmlRoot(ElementName="Samples")]
public class Samples {
[XmlElement("Sample")]
public string[] Items{ get; set; }
}
I want to end of with a list like
[
"<Sample><SomeStuff><SomMoreStuff>.. </SomeMoreStuff></SomeStuff></Sample>"
"<Sample><SomeStuff><SomMoreStuff>.. </SomeMoreStuff></SomeStuff></Sample>"
]
You might want to load your Schema into the XmlDocument class and extract the inner or outer XML from it as a string.
One example could be:
var xdoc = new XmlDocument();
xdoc.LoadXml(MySchema);
var sampleNode = xdoc.SelectNodes("//sample");
var sampleText = sampleNode.ToString();
// or
var sampleText2 = sampleNode.Item(0).OuterXml;
Use debugging to check the actual value of the node, to get the right string as output.
List example:
var xdoc = new XmlDocument();
xdoc.LoadXml(MySchema);
var sampleNode = xdoc.SelectNodes("//sample");
var sampleList = new List<string>();
foreach (XmlNode item in sampleNode)
{
sampleList.Add(item.OuterXml); // or InnerXml - whatever value it is you need.
}

linq to xml not returning any results possible namespace issue?

Here are the first few lines of my XML document:
<?xml-stylesheet type="text/xsl" href="/3.0/style/exchange.xsl"?>
<ops:world-patent-data xmlns="http://www.epo.org/exchange" xmlns:ops="http://ops.epo.org" xmlns:xlink="http://www.w3.org/1999/xlink">
<ops:meta name="elapsed-time" value="83" />
<exchange-documents>
<exchange-document system="ops.epo.org" family-id="34916979" country="EP" doc-number="1726228" kind="A1">
<bibliographic-data>
<publication-reference>
<document-id document-id-type="docdb">
<country>EP</country>
<doc-number>1726228</doc-number>`
I am trying to extract the doc-number using the code below:
public class biblio
{
public string appNumber { get; set; }
}
XElement xDoc = XElement.Load(#"pathToMyXml.xml");
XNamespace xn = "http://ops.epo.org";
var bib = from exchange in xDoc.Descendants(xn + "exchange-document")
where exchange.Attribute("kind").Equals("A1")
select new biblio
{
appNumber = exchange.Element("doc-number").Value
};
However this does not return any results.
Where am I going wrong?
Thanks.
The namespace of doc-number is http://www.epo.org/exchange. It has been inherited from the root node. You need to specify that in your query. Furthermore, doc-number isn't an element - i.e. direct child - of exchange-document. It is a descendant.
XNamespace d = "http://www.epo.org/exchange";
var bib = from exchange in xDoc.Descendants(xn + "exchange-document")
where (string)exchange.Attribute("kind") == "A1"
select new biblio
{
appNumber = (string)exchange.Descendant(d + "doc-number")
};
Please note that I changed exchange.Attribute("kind").Equals("A1") to (string)exchange.Attribute("kind") == "A1" and exchange.Descendant(d + "doc-number").Value to (string)exchange.Descendant(d + "doc-number").
That prevents NullReferenceExceptions if the attribute or descendant doesn't exist.

How to Linq2Xml a webservice?

I'm calling a WebService with HttpWebRequest.Create method and read it with StreamReader, (below method do this job):
private string ReadWebMethod(string address)
{
var myRequest = (HttpWebRequest)HttpWebRequest.Create(address);
myRequest.Method = "POST";
using (var responseReader = new StreamReader(myRequest.GetResponse().GetResponseStream()))
return responseReader.ReadToEnd();
}
This method works well and output a string like this:
<ArrayOfAppObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<AppObject>
<Name>MyApp</Name>
<Image>StoreApp.png</Image>
<Version>SM2.1.0</Version>
</AppObject>
</ArrayOfAppObject>
Now I want to have a look in this string, So I use XElemnet and parse the string. (below code):
XElement x = XElement.Parse(ReadWebMethod(address));
Now, When I foreach, x.Elements("AppObject"), it doesnt return any item and skip the foreach.
My foreach is like this:
foreach (var item in x.Elements("AppObject"))
{
listApplication.Add(new AppObject { Image = item.Element("Image").Value, Name = item.Element("Name").Value, Version = item.Element("Version").Value });
}
I create a local string and remove attributes after "ArrayOfAppObject" (xmlns:xsi="htt...)(some where name it xmlnamespace) and test it again, And it works as well and foreach does not skipped!
SO, How can I use the xml with namespace?
use XDocument class
using System.Xml.Linq;
//...
var xml = ReadWebMethod(address);
var xdocument = System.Xml.Linq.XDocument.Parse(xml);
updates
as your XML data define the namespace.. xmlns="http://tempuri.org/"
You must declare full XName with valid namespace. to access each element value
XName theElementName = XName.Get("AppObject", "http://tempuri.org/");
//or alternate way..
XName theElementName = XName.Get("{http://tempuri.org/}AppObject");
here 's sample test method
[TestMethod]
public void ParseXmlElement()
{
XDocument xdoc = XDocument.Parse(this.mockXml);
XName appTag = XName.Get("{http://tempuri.org/}AppObject");
XName nameTag = XName.Get("{http://tempuri.org/}Name");
XName imageTag = XName.Get("{http://tempuri.org/}Image");
XElement objElement = xdoc.Root.Element(appTag);
Assert.IsNotNull(objElement, "<AppObject> not found");
Assert.AreEqual("{http://tempuri.org/}AppObject", objElement.Name);
XElement nameElement = objElement.Element(nameTag);
Assert.IsNotNull(nameElement, "<Name> not found");
Assert.AreEqual("MyApp", nameElement.Value);
XElement imageElement = objElement.Element(imageTag);
Assert.IsNotNull(imageElement, "<Image> not found");
Assert.AreEqual("StoreApp.png", imageElement.Value);
}
using Xml.Linq this way..
[TestMethod]
public void ParseXmlLinq()
{
XDocument xdoc = XDocument.Parse(this.mockXml);
XElement app = xdoc.Root.Elements()
.FirstOrDefault(e => e.Name == XName.Get("AppObject", "http://tempuri.org/"));
Assert.IsNotNull(app, "<AppObject> not found");
XElement img = app.Elements()
.FirstOrDefault(x => x.Name == XName.Get("Image", "http://tempuri.org/"));
Assert.IsNotNull(img, "<Image> not found");
Assert.AreEqual("StoreApp.png", img.Value);
}
Note that I just mock up and parse string from your XML.

Filtering your LINQ to XML query

I have the following:
XDocument xdoc = XDocument.Load("C:\\myfile.xml");
List<Tag> list = new List<Tag>();
foreach (var tag in xdoc.Descendants("META"))
{
string name = tag.Attribute("name").Value;
string value = tag.Element("value").Value;
list.Add(new Tag { Name = name, Value = value, Score = score, Key = key });
}
but I need to only get the META elements under the element ARTICLE.
Can I add this to the linq query somehow?
The xml looks similar to this:
<DOCUMENT>
<META name="aaa"/>
<ARTICLE>
<META name="bbb" value="708" score="0.58" key="6008"/>
</ARTICLE>
</DOCUMENT>
Thanks for any suggestions
In the end I need to do something like the following:
XDocument xdoc = XDocument.Load(tr);
var meta = from el in xdoc.Descendants("ARTICLE").Elements("META")
where (string) el.Attribute("name") == "RAW"
select el;
List<Tag> list = new List<Tag>();
foreach (var tag in meta)
{
string name = tag.Attribute("name").Value;
string value = tag.Attribute("value").Value;
string score = tag.Attribute("score").Value;
string key = tag.Attribute("key").Value;
list.Add(new Tag { Name = name, Value = value, Score = score, Key = key });
}
The reason for this is that I needed to match the attribute where the name was equal to RAW.
Please correct me if there's a better way to do this!
To find them anywhere in the document, use xdoc.Descendants("ARTICLE") to find all the ARTICLE elements, and then Elements("META") from there to find all the direct META children elements.
In addition, you can perform the projection and the conversion to a list in the same query:
var list = xdoc.Descendants("ARTICLE")
.Elements("META")
.Select(x => new Tag { Name = (string) x.Attribute("name"),
Value = (string) x.Attribute("value"),
Key = key })
.ToList();

Categories

Resources