I'm trying to get a List of Styles in the following xml file using xdoc and LINQ.
<?xml version="1.0" encoding="UTF-8"?>
<kml>
<Document>
<Style id="style62">
<IconStyle>
<Icon>
<href>http://maps.gstatic.com/mapfiles/ms2/micons/yellow-dot.png</href>
</Icon>
</IconStyle>
</Style>
</Document>
</kml>
I cannot get my syntax right in order to get the ID="style62" AND also the value within href in the same LINQ select, can anyone help?
var styles = xdoc.Descendants(ns + "Style")
.Select(s => new
{
//HELP!?!
//E.G
//
//id = s.something (style62)
//href = s.something (url)
}).ToList();
if you are talking about a kml file like here https://developers.google.com/kml/documentation/KML_Samples.kml
then below code should work. The problem here is that every "Style" does not contain "href" tag.
var xDoc = XDocument.Parse(xml);
XNamespace ns = "http://www.opengis.net/kml/2.2";
var items = xDoc.Descendants(ns + "Style")
.Select(d =>
{
var h = d.Descendants(ns + "href").FirstOrDefault();
return new
{
Id = d.Attribute("id").Value,
Href = h == null ? null : h.Value
};
})
.ToList();
With a simple extension method, you can simplify the query
XNamespace ns = "http://www.opengis.net/kml/2.2";
var items = xDoc.Descendants(ns + "Style")
.Select(d => new
{
Id = d.Attribute("id").Value,
HRef = d.Descendants(ns + "href").FirstOrDefault()
.IfNotNull(h=>h.Value)
})
.ToList();
public static class S_O_Extensions
{
public static S IfNotNull<T, S>(this T obj,Func<T,S> selector)
{
if (obj == null) return default(S);
return selector(obj);
}
}
Something like this should work:
xdoc.Descendants(ns + "Style")
.Select(s => new
{
id = s.Attribute("id").Value,
href = s.Element("IconStyle")
.Element("Icon")
.Element("href")
.Value
});
Run this through LinqPad:
XDocument doc = XDocument.Parse("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<kml>" +
"<Document>" +
"<Style id=\"style62\">" +
"<IconStyle>" +
"<Icon>" +
"<href>http://maps.gstatic.com/mapfiles/ms2/micons/yellow-dot.png</href>" +
"</Icon>" +
"</IconStyle>" +
"</Style>" +
"</Document>" +
"</kml>");
var styles = from document in doc.Root.Elements("Document")
from style in document.Elements("Style")
where style.Attribute("id").Value == "style62"
select new
{
StyleElement = style,
Href = style.Element("IconStyle").Element("Icon").Element("href").Value
};
styles.Dump();
you can use linq like
var items = doc.Descendants("field")
.Where(node => (string)node.Attribute("name") == "Name")
.Select(node => node.Value.ToString())
.ToList();
Related
I am trying to parse an xml document with Linq and Lambda expression, but need help.
The Node from within which I want to get data is "DiskDriveInfo" ,
I'm also not sure as to how to proceed with the next node "ResultCode i:nil="true" "
My Code:
var xml = XDocument.Parse(InXML);
var r = from x in xml.Elements("DiskDriveInfo")
select new
{
ResultCode = x.Element("ResultCode").Value,
ResultCodeDescription =
x.Element("ResultCodeDescription").Value,
AirbagDetails = x.Element("AirbagDetails").Value,
..
..
WheelBase = x.Element("WheelBase").Value
};
and the input is :
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Body>
<GetConvergedDataRequestResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autoinsight.trn.co.za/types">
<ConvergedData xmlns:d4p1="http://schemas.datacontract.orgB2B.BusinessModels" i:type="ConvergedResults">
<AccidentHistory i:nil="true" />
<AlertInfo i:nil="true" />
<CloneInfo i:nil="true" />
<DiskDriveInfo>
<ResultCode i:nil="true" />
<ResultCodeDescription i:nil="true" />
<AirbagDetails>DRIVER, PASSENGER</AirbagDetails>
...
...
<WheelBase>2460</WheelBase>
</DiskDriveInfo>
Thx
There are two problems here:
Your elements are in the namespace "http://autoinsight.trn.co.za/types" but you're looking for them without specifying a namespace
You're using xml.Elements which will only look for root elements; to look for any descendants, you should use Descendants.
So you probably want:
XNamespace ns = "http://autoinsight.trn.co.za/types";
var xml = XDocument.Parse(InXML);
var r = from x in xml.Descendants(ns + "DiskDriveInfo")
select new
{
ResultCode = x.Element(ns + "ResultCode").Value,
ResultCodeDescription = x.Element(ns + "ResultCodeDescription").Value,
AirbagDetails = x.Element(ns + "AirbagDetails").Value,
..
..
WheelBase = x.Element(ns + "WheelBase").Value
};
As a side note, I probably wouldn't use a query expression for this - I'd just call Select directly:
var r = xml
.Descendants(ns + "DiskDriveInfo")
.Select(x => new
{
ResultCode = x.Element(ns + "ResultCode").Value,
ResultCodeDescription = x.Element(ns + "ResultCodeDescription").Value,
AirbagDetails = x.Element(ns + "AirbagDetails").Value,
..
..
WheelBase = x.Element(ns + "WheelBase").Value
});
If you need an element with i:nil="true" to return null instead of an empty string, I'd add an extension method for XElement:
private static XNamespace SchemaNamespace = "http://www.w3.org/2001/XMLSchema-instance";
public static string ValueOrNull(this XElement element)
{
XAttribute nil = element.Attribute(SchemaNamespace + "nil");
return (string) nil == "true" ? null : element.Value;
}
Then call it like this:
XNamespace ns = "http://autoinsight.trn.co.za/types";
var xml = XDocument.Parse(InXML);
var r = from x in xml.Descendants(ns + "DiskDriveInfo")
select new
{
ResultCode = x.Element(ns + "ResultCode").ValueOrNull(),
ResultCodeDescription = x.Element(ns + "ResultCodeDescription").ValueOrNull(),
AirbagDetails = x.Element(ns + "AirbagDetails").ValueOrNull(),
..
..
WheelBase = x.Element(ns + "WheelBase").ValueOrNull()
};
You can write from below code just you need to create class according to you xml file and below is function to convert directly xml to class object
public T DeserializeData(string dataXML)
{
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(dataXML);
XmlNodeReader xNodeReader = new XmlNodeReader(xDoc.DocumentElement);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
var modelData = xmlSerializer.Deserialize(xNodeReader);
T deserializedModel = (T)modelData ;
return deserializedModel;
}
I am attempting to read a GML file in C#. I would like to store all the returned data in one object. So far I have been able to return all the data but in 3 separate objects:
XDocument doc = XDocument.Load(fileName);
XNamespace gml = "http://www.opengis.net/gml";
// Points
var points = doc.Descendants(gml + "Point")
.Select(e => new
{
POSLIST = (string)e.Element(gml + "pos")
});
// LineString
var lineStrings = doc.Descendants(gml + "LineString")
.Select(e => new
{
POSLIST = (string)e.Element(gml + "posList")
});
// Polygon
var polygons = doc.Descendants(gml + "LinearRing")
.Select(e => new
{
POSLIST = (string)e.Element(gml + "posList")
});
I would like to instead of creating 3 separate objects, I would like to create one object as follows:
var all = doc.Descendants(gml + "Point")
doc.Descendants(gml + "LineString")
doc.Descendants(gml + "LinearRing")....
But need some help. Thanks before hand.
Sample Data:
<gml:Point>
<gml:pos>1 2 3</gml:pos>
</gml:Point>
<gml:LineString>
<gml:posList>1 2 3</gml:posList>
</gml:LineString>
<gml:LinearRing>
<gml:posList>1 2 3</gml:posList>
</gml:LinearRing>
You can use Concat:
XDocument doc = XDocument.Load(fileName);
XNamespace gml = "http://www.opengis.net/gml";
var all = doc.Descendants(gml + "Point")
.Concat(doc.Descendants(gml + "LineString"))
.Concat(doc.Descendants(gml + "LinearRing"));
For getting the values as the inner elements you can do something like this:
XDocument doc = XDocument.Load("data.xml");
XNamespace gml = "http://www.opengis.net/gml";
var all = doc.Descendants(gml + "Point")
.Select(e => new { Type = e.Name, Value = (string)e.Element(gml + "pos") })
.Concat(doc.Descendants(gml + "LineString")
.Select(e => new { Type = e.Name, Value = (string)e.Element(gml + "posList") }))
.Concat(doc.Descendants(gml + "LinearRing")
.Select(e => new { Type = e.Name, Value = (string)e.Element(gml + "posList") }));
I need assistance in retrieving the element Country and Its value 1 in the below code.
Thanks in advance.
<?xml version="1.0" encoding="utf-8"?>
<env:Contentname xmlns:env="http://data.schemas" xmlns="http://2013-02-01/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<env:Content action="Hello">
<env:Data xsi:type="Yellow">
</env:Data>
</env:Content>
<env:Content action="Hello">
<env:Data xsi:type="Red">
<Status xmlns="http://2010-10-10/">
<Id >39681</Id>
<Name>Published</Name>
</Status>
</env:Data>
</env:Content>
<env:Content action="Hello">
<env:Data xsi:type="green">
<Document>
<Country>1</Country>
</Document>
</env:Data>
</env:Content>
</env:Body>
</env:Contentname>
I tried this,
var result = from x in root.Descendants(aw + "Data")
where (string)x.Attribute(kw + "type") == "green"
select x;
foreach (var item in result)
{
var str = item.Element("Document").Element("Country");
Console.WriteLine(str.Value);
}
But i am getting error.(Object reference not set to an instance of an object.)
kindly help me with this.
This is the problem, for two reasons:
var str = item.Element("Document").Element("country");
Firstly, XML is case-sensitive - you want Country, not country.
Secondly, those elements inherit the namespace declared with xmlns=... in the root element. You need:
XNamespace ns = "http://2013-02-01/";
...
var element = item.Element(ns + "Document").Element(ns + "Country");
I'd also encourage you to avoid query expressions where they don't actually buy you much. In this case, you could perform the whole query in one go using the Elements extension method which works on sequences of input elements, assuming you don't mind finding every Document -> Country element rather than just one per Data:
var query = root.Descendants(aw + "Data")
.Where(x => (string)x.Attribute(kw + "type") == "green")
.Elements(ns + "Document")
.Elements(ns + "Country")
.Select(x => x.Value);
foreach (var item in query)
{
Console.WriteLine(item);
}
One significant difference - this won't fall over with an exception if there is a Data element with xsi:type='green' which doesn't have a Document -> Country element. If you want it to (to find bad data) you could use:
var query = root.Descendants(aw + "Data")
.Where(x => (string)x.Attribute(kw + "type") == "green")
.Select(x => x.Element(ns + "Document")
.Element(ns + "Country")
.Value);
To show a short but complete example, this code (with your XML as test.xml) gives an output of "1":
using System;
using System.Linq;
using System.Xml.Linq;
public class Program
{
static void Main(string[] args)
{
var doc = XDocument.Load("test.xml");
XNamespace ns = "http://2013-02-01/";
XNamespace kw = "http://www.w3.org/2001/XMLSchema-instance";
XNamespace aw = "http://data.schemas";
var query = doc.Descendants(aw + "Data")
.Where(x => (string)x.Attribute(kw + "type") == "green")
.Elements(ns + "Document")
.Elements(ns + "Country")
.Select(x => x.Value);
foreach (var item in query)
{
Console.WriteLine(item);
}
}
}
I have a word Document in Xml format with multiple entries like so :
<aml:annotation aml:id="0" w:type="Word.Bookmark.Start" w:name="CustomerName"/>
I want to retrieve a collection of these but cannot see how to get past
foreach (XElement ann in doc.Root.Descendants(aml + "annotation"))
In other words I can get all annotations, but cannot see how to filter to just retrieve bookmarks. The namespaces aml and w are declared like this
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
XNamespace aml = "http://schemas.microsoft.com/aml/2001/core";
Could someone give me a push ?
I resolved the issue as follows
XNamespace w = doc.Root.GetNamespaceOfPrefix("w");
XNamespace aml = doc.Root.GetNamespaceOfPrefix("aml");
foreach (string bookm in doc.Descendants(aml + "annotation")
.Where(e => e.Attributes(w + "type")
.Any(a => a.Value == "Word.Bookmark.Start"))
.Select(b => b.Attribute(w + "name").Value))
{
...
}
var names = from a in doc.Root.Descendants(aml + "annotation"))
where (string)a.Attribute(w + "type") == "Word.Bookmark.Start"
select (string)a.Attribute(w + "name");
Lambda syntax:
doc.Root.Descendants(aml + "annotation")
.Where(a => (string)a.Attribute(w + "type") == "Word.Bookmark.Start")
.Select(a => (string)a.Attribute(w + "name"))
This solution is not for XML but maybe helps you.
System.Collections.Generic.IEnumerable<BookmarkStart> BookMarks = wordDoc.MainDocumentPart.RootElement.Descendants<BookmarkStart>();
foreach (BookmarkStart current in BookMarks)
{
//Do some...
}
I have this LINQ query:
XNamespace ns = NAMESPACE;
var items = (from c in doc.Descendants(ns +"Item")
select new Item
{
Title = c.Element(ns + "ItemAttributes").Element(ns + "Title").Value,
MFR = c.Element(ns + "ItemAttributes").Element(ns + "Manufacturer").Value,
Offer = c.Element(ns + "Offers").Element(ns + "TotalOffers").Value,
Amazon = c.Element(ns + "Offer").Element(ns + "Merchant").Elements(ns + "MerchantId"),
LowPrice = Convert.ToDouble(c.Element(ns + "FormattedPrice").Value),
SalesRank = Convert.ToInt32(c.Element(ns +"SalesRank").Value),
ASIN = c.Element(ns + "ASIN").Value
}).ToList<Item>();
It works great expect for when a node is not present. For example it my not have a MFR or a sales rank. How can I make it so if it does not have the node in question, it gives me a default value or at the very doesn't make me try catch my whole query for one item.
As far as I'm aware LINQ to XML doesn't support this. However I ran into this same mess in a project I was working on and created this extension for XElement to allow it. Maybe it could work for you:
public static XElement ElementOrDummy(this XElement parentElement,
XName name,
bool ignoreCase)
{
XElement existingElement = null;
if (ignoreCase)
{
string sName = name.LocalName.ToLower();
foreach (var child in parentElement.Elements())
{
if (child.Name.LocalName.ToLower() == sName)
{
existingElement = child;
break;
}
}
}
else
existingElement = parentElement.Element(name);
if (existingElement == null)
existingElement = new XElement(name, string.Empty);
return existingElement;
}
Basically it just checks to see if the element exists and if it doesn't it returns one with the same name and an empty value.
You can use XElement Explicit Conversion, e.g.:
(int?)c.Element(ns +"SalesRank")
Reference: http://msdn.microsoft.com/en-us/library/bb340386.aspx
if the problem that the XElement exists, but the value is blank? i.e.
<Item>
<ItemAttributes>
<Manufacturer></Manufacturer>
</ItemAttributes>
</Item>
then you can use the string.IsNullOrEmpty function
XNamespace ns = NAMESPACE;
var items = (from c in doc.Descendants(ns +"Item")
select new Item
{
MFR = if (string.IsNullOrEmpty(c.Element(ns + "ItemAttributes").Element(ns + "Manufacturer").Value)) ? "default value here" : c.Element(ns + "ItemAttributes").Element(ns + "Manufacturer").Value,
// omitted for brevity
}).ToList<Item>();