How to get array of subelements from an element? - c#

My xml looks likes this
<stock name="Fish">
<eqn>100</eqn>
<inflow>Spawns</inflow>
<outflow>Deaths</outflow>
<outflow>Flow_1</outflow>
<non_negative/>
</stock>
My code looks likes this
var stockList = from q in variable.Descendants(ns + "stock")
select new {
name = q.FirstAttribute.Value,
initial = q.Element(ns + "eqn").Value,
inflow = q.Element(ns + "inflow").Value,
outflow = q.Element(ns + "outflow").Value
};
This only gives me the first inflow and first outflow, but there are two outflows so I need to be store each one in an array.

You can use the public IEnumerable<XElement> Elements (XName name) method. It returns all the child elements of this element having a matching name.
outflow = q.Elements(ns + "outflow").Select(el => el.Value).ToArray()
or
outflow = (from el in q.Elements(ns + "outflow") select el.Value).ToArray()

Related

XML Response to Linq

My XML response looks like this:
"<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>
<HotelAvailResponse xmlns="http://www.juniper.es/webservice/2007/">
<AvailabilityRS Url="http://xml.bookingengine.es"
TimeStamp="20130327T14:38:54.6916528+01:00"
IntCode="lCf65bPrG+x7VDLB0IquVNQgKloRA9+HOuhfHMj0BcE=">
<Results>
<HotelResult Code="7b0LYEzfsd0HH90sd" JPCode="JP003600" DestinationZone="39303" JPDCode="JPD000014">
<HotelInfo>
<Name>Hotel Test</Name>
<Description>A spacious, quiet, light and bright hotel, with a garden and fabulous views across the city.</Description>
<Images>
<Image>http://www.bookingengine.es/images/upload_p/hoteltest.jpeg</Image>
</Images>
<HotelCategory>1 1/2 Stars</HotelCategory>
<HotelType Type="SKI">Snow</HotelType>
<Address>c/tap</Address>
</HotelInfo>
<HotelOptions>
<HotelOption RatePlanCode="dcFZbKty1cJGKeRtgxIDGUZAprp1mua8ySl4iVIZ7NVKBF/PGk8lhZlN7Hcszjs2RwUR2Dxsrv5l0cZDORKz6frEmPdibqOyV2Jg4Dxz8/bF5gqPyQR8+z1LEu8LCVlS" Status="OK">
<Board Type="AD">Bed&Breakfast</Board>
<Prices>
<Price Type="S" Currency="USD">
<TotalFixAmounts Gross="353.65" Nett="353.65">
<Service Amount="321.5"/>
<ServiceTaxes Included="true" Amount="32.15"/>
<Commissions Included="true" Amount="0"/>
<HandlingFees Included="true" Amount="0"/>
<Discount Amount="-0"/>
</TotalFixAmounts>
</Price>
</Prices>
<HotelRooms>
<HotelRoom Units="1" Source="1" AvailRooms="12">
<Name>Double Room</Name>
<RoomCategory Type="DBL">Double Standard</RoomCategory>
</HotelRoom>
<HotelRoom Units="1" Source="2" AvailRooms="45">
<Name>Single</Name>
<RoomCategory Type="SGL">Single Standard</RoomCategory>
</HotelRoom>
</HotelRooms>
</HotelOption>
<HotelOption RatePlanCode="dcFZbKty1cJGKeRtgxIDGUZAprp1mua8ySl4iVIZ7NVKBF/PGk8lhZlN7Hcszjs2RwUR2Dxsrv5l0cZDORKz6frEmPdibqOyV2Jg4Dxz8/bmoX041DU9+3D3nHCEB/6vYKbVtJR2qaHwW9VnnWl/KA==" Status="OK">
<Board Type="AD">Bed&Breakfast</Board>
<Prices>
<Price Type="S" Currency="USD">
<TotalFixAmounts Gross="353.65" Nett="353.65">
<Service Amount="321.5"/>
<ServiceTaxes Included="true" Amount="32.15"/>
<Commissions Included="true" Amount="0"/>
<HandlingFees Included="true" Amount="0"/>
<Discount Amount="-0"/>
</TotalFixAmounts>
</Price>
</Prices>
<HotelRooms>
<HotelRoom Units="1" Source="1" AvailRooms="12">
<Name>Double Room</Name>
<RoomCategory Type="DBL">Double Standard</RoomCategory>
</HotelRoom>
<HotelRoom Units="1" Source="2" AvailRooms="11">
<Name>Double Room</Name>
<RoomCategory Type="DBL">Double Standard</RoomCategory>
</HotelRoom>
</HotelRooms>
<AdditionalElements>
<HotelOffers>
<HotelOffer>
<Name>Basic Discount 10%</Name>
</HotelOffer>
</HotelOffers>
</AdditionalElements>
</HotelOption>
</HotelOptions>
</HotelResult>
</Results>
</AvailabilityRS>
</HotelAvailResponse>
</soap:Body>
</soap:Envelope>"
And i have this Linq for that response is as follows:
XNamespace ns = "http://schemas.xmlsoap.org/soap/envelope/";
var hotels = (from hotelData in data.Descendants(ns + "Envelope").Descendants(ns + "Body").Descendants("HotelAvailResponse").Descendants("HotelAvailResult").Descendants("Results").Descendants("HotelResult")
select new Hotel
{
Code = hotelData.Attribute("Code").Value,
JpCode =
hotelData.Attributes().Any(x => x.Name == "JPCode")
? hotelData.Attribute("JPCode").Value
: "",
DestinationZone =
hotelData.Attribute("DestinationZone") != null
? hotelData.Attribute("DestinationZone").Value
: string.Empty,
JpdCode = hotelData.Attribute("JPDCode").Value,
//HotelName = Convert.ToString(hotelData.Element("Item").Value),
//Rating = Convert.ToInt32(hotelData.Element("StarRating").Value),
HotelInfo = (from hi in hotelData.Descendants("HotelInfo")
select new HotelInfo
{
Name = hi.Element("Name").Value,
Description = hi.Element("Description").Value,
Image = (from img in hi.Descendants("Images") select new Imagez { Images = img.Element("Image").Value }).ToList(),
HotelCategory = hi.Element("Name").Value,
HotelType = hi.Element("Description").Value,
Address = hi.Element("Description").Value,
}
).ToList(),
HotelOptions = (from ho in hotelData.Descendants("HotelOptions")
select new HotelOptions()
{
HotelOption = ho.Element("HotelOption").Attribute("RatePlanCode").Value,
Board = ho.Element("Board").Attribute("Type").Value,
Prices = (from pr in ho.Descendants("Prices") select new Prices() { Price = pr.Element("Price").Value,
TotalFixAmounts = (from tfa in pr.Descendants("TotalFixAmounts") select new TotalFixAmounts()
{ Service = tfa.Element("Service").Attribute("Amount").Value,
ServiceTaxes = tfa.Element("ServiceTaxes").Attribute("Included").Value,
AmountServiceTaxes = tfa.Element("ServiceTaxes").Attribute("Amount").Value,
Commissions = tfa.Element("Commissions").Attribute("Included").Value,
AmountCommissions = tfa.Element("Commissions").Attribute("Amount").Value,
HandlingFees = tfa.Element("HandlingFees").Attribute("Included").Value,
AmountHandlingFees = tfa.Element("HandlingFees").Attribute("Amount").Value,
Discount = tfa.Element("Amount").Attribute("Included").Value,
}).ToList(),
}).ToList(),
}).ToList(),
}).ToList();
return hotels;
I get no error and neither any exception but the count for hotels returned is 0.
I am a beginner at Linq.
Any help will be much appreciated, and its been 7 hours straight, I am searching for help and trying to get it done. Now I feel the dead end.
Thanks in advance.
There are a few issues with your posted code.
The first issue is that the code does not account for the namespace assigned to <HotelResult>. The namespace is http://wwww.juniper.es/webservice/2007/, and is inherited from the <HotelAvailResponse> element. You can see the namespace as an xmlns attribute:
<HotelAvailResponse xmlns="http://www.juniper.es/webservice/2007/">
This is the reason your LINQ query is not returning anything - it's looking for <HotelAvailResponse> with the XML Namespace http://schemas.xmlsoap.org/soap/envelope, and that node does not exist, so you get an empty collection. Each time there is a call to Descendants or Element, the namespace needs to be included (i.e., ns + "ElementName").
The second issue isn't readily apparent until the code acutally runs, but the following statement
HotelOptions = (from ho in hotelData.Descendants(ns + "HotelOption")
will result in <HotelOption> and <Board> appearing only once (i.e., a list of 1) rather than twice. The <price> information and <TotalFixAmounts> populates correctly - I'm not entirely sure why, but it may have to do with the nested list for <TotalFixAmounts>. This is easily fixed by changing the select to <HotelOption>, like this:
HotelOption = (from ho in hotelData.Descendants("HotelOption")
Now ho will be a collection of <HotelOption> nodes and their children, both will be processed and the nested list will be processed as well.
Next there are a couple of problems with your LINQ statement that will throw null reference exceptions (assuming the namespace issue is corrected). These are:
Board = ho.Element("Board").Attribute("Type").Value;
ho is the collection of all the <HotelOptions> nodes and their children - but <Board> is a child of <HotelOption>, which in itself is a child of <HotelOptions>. When working with XML, remember it's hierarchical in nature - .Element(elementName) will access the first element with that name that is a child (not a grand child or farther down) of the parent element. A fairly simple solution is to add <HotelOption> into the statement:
Board = ho.Element(ns + "HotelOption").Element(ns + "Board").Attribute("Type").Value;
A similar issue occurs here:
TotalFixAmounts = (from tfa in pr.Descendants("Prices") select new
pr is the collection of <Prices> nodes, but the elements being referenced in the select statement are children of <Price>, not <Prices>. Element(elementName) will get the first child node of the parent element, and <Prices> does not have any children other than <Price>.
Finally, there is no <Amount> element that is a child of <TotalFixAmounts>, so the following line will also throw a null reference exception:
Discount = tfa.Element("Amount").Attribute("Included").Value;
In place of the two ternary operators you use, I would recommend using (string) - the explicit cast will safely handle missing elements or attributes. If the element or attribute is missing, the code will not fail, it will simply have no value for that property.
So, putting this all together you get this:
XNamespace ns = "http://www.juniper.es/webservice/2007/";
var hotels = (from hotelData in data.Root.Descendants(ns + "HotelResult")
select new Hotel
{
Code = (string)hotelData.Attribute("Code"),
JpCode = (string)hotelData.Attribute("JPCode"),
DestinationZone = (string)hotelData.Attribute("DestinationZone"),
JpdCode = (string)hotelData.Attribute("JPDCode"),
HotelInfo = (from hi in hotelData.Descendants(ns + "HotelInfo")
select new HotelInfo
{
Name = (string)hi.Element("Name"),
Description = (string)hi.Element(ns + "Description"),
Image = (from img in hi.Descendants(ns + "Images")
select new Imagez
{
Images = (string)img.Element(ns + "Image")
}).ToList(),
HotelCategory = (string)hi.Element(ns + "Name"),
HotelType = (string)hi.Element(ns + "Description"),
Address = (string)hi.Element(ns + "Description"),
}).ToList(),
HotelOptions = (from ho in hotelData.Descendants(ns + "HotelOption")
select new HotelOptions
{
HotelOption = ho.Attribute("RatePlanCode").Value,
Board = ho.Element(ns + "Board").Attribute("Type").Value,
Prices = (from pr in ho.Descendants(ns + "Prices")
select new Price
{
Price = (string)pr.Element(ns + "Price"),
TotalFixAmounts = (from tfa in pr.Descendants(ns + "Price").Descendants(ns + "TotalFixAmounts")
select new TotalFixAmounts
{
Service = tfa.Element(ns + "Service").Attribute("Amount").Value,
ServiceTaxes = tfa.Element(ns + "ServiceTaxes").Attribute("Included").Value,
AmountServiceTaxes = tfa.Element(ns + "ServiceTaxes").Attribute("Amount").Value,
Commissions = tfa.Element(ns + "Commissions").Attribute("Included").Value,
AmountCommissions = tfa.Element(ns + "Commissions").Attribute("Amount").Value,
HandlingFees = tfa.Element(ns + "HandlingFees").Attribute("Included").Value,
AmountHandlingFees = tfa.Element(ns + "HandlingFees").Attribute("Amount").Value
}).ToList(),
}).ToList(),
}).ToList(),
}).ToList();
Note that every reference to Descendants or Element has the namespace ns in it.

Linq to XML Descendants can't read part of xml

I have XML document like this
<document>
<indexroot>
<type>type</type>
<model>model</model>
</indexroot>
<root>
<model_type1>model type 1</model_type1>
<model_type2>model type 2</model_type2>
</root>
</document>
And a linq to xml code:
var elements = (from element in pdb.Descendants()
select new
{
type = (string)element.Element("type") ?? "-",
model= (string)element.Element("model") ?? "-",
model_type1= (string)element.Element("model_type1") ?? "-",
model_type2= (string)element.Element("model_type2") ?? "-"
}).FirstOrDefault();
I get type and a model variables, but it seems I can't reach model_type1 and model_type2, now I understand that this happens because indexroot and root tags, amd if I seperate those tags into diffrent linq to xml code blocks with Descendants("indexroot") and Descendants("root"), everything works fine, but I wan't them in one block, is it possible to achieve that, and how?
You need to navigate down the XML heirarchy for each element you are trying to extract.
This is because the Element method only looks at direct children, not all descendants of a node. From the documentation:
Gets the first (in document order) child element with the specified XName.
One implementation using just Linq-to-XML might be:
xml = "<document>" +
"<indexroot>" +
" <type>type</type>" +
" <model>model</model>" +
"</indexroot>" +
"<root>" +
" <model_type1>model type 1</model_type1>" +
" <model_type2>model type 2</model_type2>" +
"</root>" +
"</document>";
XDocument doc = XDocument.Parse(xml);
var newItem = (from element in doc.Descendants("document")
select new
{
Type = (string)element.Element("indexroot").Element("type") ?? "-",
Model = (string)element.Element("indexroot").Element("model") ?? "-",
ModelType1 = (string)element.Element("root").Element("model_type1") ?? "-",
ModelType2 = (string)element.Element("root").Element("model_type2") ?? "-",
}).FirstOrDefault();
Console.WriteLine(newItem);

simple way to read xml data using linq

I have a xml structure like this. Can anyone help with a simple linq function to read this xml structure.The itemEntry node repeats according to data. I tried to read the xml using the method below,but i am getting no records in the list. Is this method here correct way to get the details...
List<CX_ITEMLIST> sList =
(from e in XDocument.Load(param.FileName).Root.Elements("itemEntry")
select new CX_ITEMLIST
{
TITLE = (string)e.Element("title"),
YEAR = (string)e.Element("year"),
ITEMNAME = (string)e.Element("itemname"),
CATRYLIST =
(
from p in e.Elements("categorylist").Elements("categories")
select new CATLIST
{
IDTYPE = (string)p.Element("categoryid"),
IDNUMBER = (string)p.Element("categoryName")
}).ToList()
}).ToList();
<itemslist>
<itemInformation>
<itemdate>01/23/2014</itemdate>
<itemcount>57</itemcount>
</itemInformation>
<itemEntry>
<title>Title1</title>
<year>2013</title>
<itemname>testname</itemname>
<categorylist>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
</categorylist>
</itemEntry>
<itemEntry>
<title>Title1</title>
<year>2013</title>
<itemname>testname</itemname>
<categorylist>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
</categorylist>
</itemEntry>
</itemslist>
You should try with XDocument.
XDocument xdoc = XDocument.Load("file.xml");
The System.Xml.XLinq namespace contains some awesome objects to make this easy.
var xDoc = XDocument.Parse(xml); // load your xml string, or use XDocument.Load() to load an xml file
var itemEntries = xDoc
.Root // refers to itemEntries node
.Descendants("itemEntry"); // gets all itemEntry nodes in an IEnumerable object
This gets you an IEnumerable<XNode> of all the itemEntry nodes.
From there you can do what you need, save the values to a business object, etc.
The above method works properly, i found the issue, my xml tag was having namespace attribute. i tried to get the namespace and append it with Elements while reading
XNamespace ns = xDocument.Root.Attribute("xmlns").Value;
List<CX_ITEMLIST> sList =
(from e in XDocument.Load(param.FileName).Root.Elements(ns + "itemEntry")
select new CX_ITEMLIST
{
TITLE = (string)e.Element(ns + "title"),
YEAR = (string)e.Element(ns + "year"),
ITEMNAME = (string)e.Element(ns + "itemname"),
CATRYLIST =
(
from p in e.Elements(ns + "categorylist").Elements(ns + "categories")
select new CATLIST
{
IDTYPE = (string)p.Element(ns + "categoryid"),
IDNUMBER = (string)p.Element(ns + "categoryName")
}).ToList()
}).ToList();

How to get an specific node in xml with namespaces?

I'm dealing to access an specific node from a XML Document. I realized that this one as a base namespace. Here is the example.
I'm interested to get the value of the node d:MediaUrl from all descendents node (entry). And I haven't accomplished that.
When I debug the variable iterator 'i', I can see that the XML includes the default namespace again, something like:
<entry xmlns="http://schemas.microsoft.com.ado/..."
And also I have to include the another namespace called 'd'.
What can I do to access to that particular nodes?
This is what I have.
var doc = XDocument.Parse(result);
string BASE_NS = "http://www.w3.org/2005/Atom";
string d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
var query = from i in doc.Descendants(XName.Get("entry", BASE_NS))
select new Image()
{
Url = i.Element(XName.Get("MediaUrl", BASE_NS)).Value
};
var results = query.ToList();
I would suggest using XNamespace rather than XName (personal preference, mainly - as that's how I've always dealt with namespaces in LINQ to XML). To me it's less effort to set up the namespaces in advance and then use Element(NS + "element name") than to useXName.Get(though usingXName.Get` is perfectly fine if that's what you want to do.
If you want to get a all the "MediaUrl" elements for each entry, then I'd do something like this:
XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
var query = (from i in doc.Descendants(d + "MediaUrl")
select new Image()
{
Url = i.Value
}).ToList();
If you want to get only one of them, then you need to do something a little different, depending on which one you wanted to get.
For the properties MediaUrl:
XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
var query = (from i in doc.Descendants(m + "properties")
select new Image()
{
Url = i.Element(d + "MediaUrl").Value
}).ToList();
For the Thumbnail MediaUrl:
XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
var query = (from i in doc.Descendants(d + "Thumbnail")
select new Image()
{
Url = i.Element(d + "MediaUrl").Value
}).ToList();
The key here is to use the namespace in conjunction with the element name in order to retrieve it.
var query = from i in doc.Descendants("{full namespace for prefix d}MediaUrl")
select new Image()
{
Url = i.Value
};

XElement.Element returning null for newly created element

I am using XElement to create an XMLDocument which is used in a hierarchical WPF treeview. If I create a new element with :
x_element = new XElement("node",
new XElement("tree_id", strData[0]),
new XElement("sys_id", dpl.DepSysId),
new XElement("part_id", strData[8]),
new XElement("make", strData[6]),
new XElement("model", strData[5]),
new XElement("level", strData[2]));
I then need to add attributes to "node" so I tried:
XElement temp_el = x_element.Element("node"); // This is returning null
temp_el.SetAttributeValue("title", strData[7] + " " + strData[6] + " " + strData[5]);
temp_el.SetAttributeValue("canEdit", "False");
temp_el.SetAttributeValue("status", nStatus.ToString());
temp_el.SetAttributeValue("qty", strData[13]);
temp_el.SetAttributeValue("part", strData[8]);
In the above code temp_el is null, but I can see in the debugger that x_element contains the following :
<node>
<tree_id>82</tree_id>
<sys_id>82</sys_id>
<part_id>169</part_id>
<make>ST Panel</make>
<model>Logical Pure 16 tube Collector</model>
<level>0</level>
</node>
To work around this I have used the following:
foreach (XElement temp_el in x_element.DescendantsAndSelf())
{
if (temp_el.Name == "node")
{
temp_el.SetAttributeValue("title", strData[7] + " " + strData[6] + " " + strData[5]);
temp_el.SetAttributeValue("canEdit", "False");
temp_el.SetAttributeValue("status", nStatus.ToString());
temp_el.SetAttributeValue("qty", strData[13]);
temp_el.SetAttributeValue("part", strData[8]);
break;
}
}
Whilst the above works I am just curious as to why I am getting null returned. Is my workaround the best way of doing this?
Regards.
You defined your XElement like this:
x_element = new XElement("node", /* child nodes */);
Where "node" is the name of the XElement you are creating, and the following parameters are its children.
By using x_element.Node("node"), you are trying to get the child node named "node", and there isn't such a child node.
x_element itself is the node named "node".
DescendantsAndSelf worked because it includes x_element (hence "AndSelf"), but you don't need this either because you already have the node.
So you can change your second code snippet to this:
x_element.SetAttributeValue("title", strData[7] + " " + strData[6] + " " + strData[5]);
x_element.SetAttributeValue("canEdit", "False");
// etc.
(BTW, you can also add the Attributes in the constructor)
Because with your first temp_el,
XElement temp_el = x_element.Element("node");
You used to get nodes which is not treated to be an Element of x_element.
It was treated as its root. However, with the second one,
x_element.DescendantsAndSelf()`
You used this XElement Method which treat node itself as an element.
XContainer.Elements Method - Returns a collection of the child elements of this element or document, in document order.
XElement.DescendantsAndSelf Method - Returns a collection of elements that contain this element, and all descendant elements of this element, in document order.
To solve the issue I used Descendants(). Here is my code snippet
public void UpdateEnquiry([FromBody]XElement UpdatePurchaseOrder)
{
var obj = XElement.Parse(UpdatePurchaseOrder.ToString());
var ii = (from v in obj.Descendants() select new { v.Value }).ToList() ;
}

Categories

Resources