I'm working on windows 8 metro apps and i need to get some information from a XML file.
i parse it with LINQ to XML but I've got a problem.
here is the XML:
<feed xmlns="http://www.allocine.net/v6/ns/">
<page>1</page>
<count>1</count>
<results type="movie">10</results>
<results type="person">0</results>
<totalResults>10</totalResults>
<movie code="61282">
<originalTitle>Avatar</originalTitle>
<title>Avatar</title>
<productionYear>2009</productionYear>
<release>
<releaseDate>2010-09-01</releaseDate>
</release>
<castingShort>
<directors>James Cameron</directors>
<actors>Sam Worthington, Zoe Saldana, Sigourney Weaver, Stephen Lang, Michelle Rodriguez</actors>
</castingShort>
<statistics>
<pressRating>4.33333</pressRating>
<userRating>4.31338</userRating>
</statistics>
<poster path="/medias/nmedia/18/78/95/70/19485155.jpg"
href="http://images.allocine.fr/medias/nmedia/18/78/95/70/19485155.jpg"/>
<linkList>
<link rel="aco:web"
href="http://www.allocine.fr/film/fichefilm_gen_cfilm=61282.html"/>
</linkList>
</movie>
</feed>
I need to get "code" value of the "movie" node and the "href" value of the node "link" but all the things i tried failed ...
You can consider that the beginning of the XML is <movie> because I get the file and i parse it to keep the XML clean as I want. my file start with <movie code="">
For a classic value like "actors" i do :
Actors = (string)query.Element("castingShort").Element("actors")
It is working perfectly! My problem is for the specific value with a name.
edit :
that's what i did with your advices.
var group1 = new SampleDataGroup("Group-1", "Films", "", "Assets/icone_groupe_all_movies.jpg", "Films sur le DD");
movieName = "avatar";
Uri = "http://api.allocine.fr/rest/v3/search?partner=YW5kcm9pZC12M3M&filter=movie,person&count=1&page=1&q=" + movieName + "&format=xml";
var httpResponse = await new HttpClient().GetAsync(Uri);
string sourceCode = get_new_xml(await httpResponse.Content.ReadAsStringAsync());
XDocument doc = XDocument.Parse(sourceCode);
XNamespace ns = "http://www.allocine.net/v6/ns/";
XElement movie = doc.Root.Element(ns + "movie");
XElement castingShort = movie.Element(ns + "castingShort");
XElement statistics = movie.Element(ns + "statistics");
Data data = new Data
{
MovieCode = (string)movie.Attribute("code"),
OriginalTitle = (string)movie.Element(ns + "originalTitle"),
Title = (string)movie.Element(ns + "title"),
ProductionYear = (string)movie.Element(ns + "productionYear"),
Directors = (string)castingShort.Element(ns + "directors"),
Actors = (string)castingShort.Element(ns + "actors"),
PressRating = (string)statistics.Element(ns + "pressRating"),
UserRating = (string)statistics.Element(ns + "userRating"),
Cover = (string)movie.Element(ns + "linkList").Element(ns + "link").Attribute("href")
};
group1.Items.Add(new SampleDataItem("Group-1-Item-1", data.Title, data.Cover, data.ProductionYear, "", data.ReleaseDate, group1));
this.AllGroups.Add(group1);
but unfortunately it still doesent work ...
Thus you have namespace declared in your xml, you should declare and initialize an XNamespace object, and to use it when specifying XName objects (arguments to Element methods):
XDocument xdoc = XDocument.Load(path_to_xml);
XNamespace ns = "http://www.allocine.net/v6/ns/";
XElement movie = xdoc.Root.Element(ns + "movie");
var code = (int)movie.Attribute("code");
var href = (string)movie.Element(ns + "linkList")
.Element(ns + "link").Attribute("href");
If you have only one <movie> element, then you don't need to operate on sequence of movie elements and treat single movie as list. Simply get movie node and create new data object via parsing that node:
XNamespace ns = "http://www.allocine.net/v6/ns/";
XElement movie = xdoc.Root.Element(ns + "movie");
XElement castingShort = movie.Element(ns + "castingShort");
XElement statistics = movie.Element(ns + "statistics");
Data data = new Data
{
MovieCode = (int)movie.Attribute("code"),
OriginalTitle = (string)movie.Element(ns + "originalTitle"),
Title = (string)movie.Element(ns + "title"),
ProductionYear = (string)movie.Element(ns + "productionYear"),
Directors = (string)castingShort.Element(ns + "directors"),
Actors = (string)castingShort.Element(ns + "actors"),
PressRating = (string)statistics.Element(ns + "pressRating"),
UserRating = (string)statistics.Element(ns + "userRating"),
Cover = (string)movie.Element(ns + "linkList")
.Element(ns + "link").Attribute("href")
};
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 have this Style XML:
<Style Name="TextBox2" Type="TEXT">
<Alignment>LEFT</Alignment>
<BorderColor>blue</BorderColor>
<BorderStyle>Solid</BorderStyle>
<BackGroundColor>red</BackGroundColor>
<Options>VISIBLE</Options>
<TextColor>Black</TextColor>
<TextSize>10</TextSize>
<MaxCharacterLength>50</MaxCharacterLength>
I want to write a function which enables me to search for a Name (e.g. Checkbox2) and returns us corresponding names and values of child nodes (Alignment=Center, Bordercolor=red etc.)
Should I use datatable or Dataset? Or any suggestion will be much appreciated
Try something like this to get only one object
string xml =
"<Root>" +
"<Style Name=\"TextBox2\" Type=\"TEXT\">" +
"<Alignment>LEFT</Alignment>" +
"<BorderColor>blue</BorderColor>" +
"<BorderStyle>Solid</BorderStyle>" +
"<BackGroundColor>red</BackGroundColor>" +
"<Options>VISIBLE</Options>" +
"<TextColor>Black</TextColor>" +
"<TextSize>10</TextSize>" +
"<MaxCharacterLength>50</MaxCharacterLength>" +
"</Style>" +
"</Root>";
XDocument doc = XDocument.Parse(xml);
var style = doc.Descendants("Style").Where(x => (string)x.Attribute("Name") == "TextBox2").Select(x => new
{
type = (string)x.Attribute("Type"),
alignment = (string)x.Element("Alignment"),
borderColor = (string)x.Element("BorderColor"),
borderStyle = (string)x.Element("BorderStyle"),
background = (string)x.Element("BackGroundColor"),
options = (string)x.Element("Options"),
textColor = (string)x.Element("TextColor"),
textSize = (string)x.Element("TextSize"),
maxCharacters = (int)x.Element("MaxCharacterLength"),
}).FirstOrDefault();
<CPT xmlns="http://www.example.org/genericClientProfile" xmlns:ns2="http://www.blahblah.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/genericClientProfile genericClientProfile.xsd">
<header>
<serviceId>CPT-UK</serviceId>
<versionId>1.0</versionId>
<brandCode>CUK</brandCode>
<creationTime>2013-09-26T13:55:32.31+02:00</creationTime>
</header>
</CPT>
I need to be able to read the elements in the header tag. I'm struggling to read the values for some reason, which i'm not sure of. What i've tried:
public ActionResult readxmldata()
{
using (var db = new CPTEntities())
{
var file = System.IO.Directory.GetFiles("C:\\Workspace\\CPTStaging","*.xml");
foreach (var xmldoc in file)
{
XmlDocument docpath = new XmlDocument();
docpath.Load(xmldoc);
CPTPROFILE doc = new CPTPROFILE();
db.SaveChanges();
H_HEADER header = new H_HEADER();
header.SERVICEID = docpath.SelectSingleNode("//CPT/header/#serviceId").Value;
header.VERSIONID = Convert.ToDecimal(docpath.SelectSingleNode("//CPT/header/#versionId").Value);
header.CREATIONTIME = Convert.ToDateTime(docpath.SelectSingleNode("//CPT/header/#creationTime").Value);
header.BRANDCODE = docpath.SelectSingleNode("//CPT/header/#brandCode").Value;
db.CPTPROFILEs.AddObject(doc);
db.SaveChanges();
}
}
Your XML uses namespaces. xmlns attribute declares default namespace. You should use it for each element of the XML.
XNamespace ns = "http://www.example.org/genericClientProfile";
XDocument doc = XDocument.Load(xmldoc);
XElement header = doc.Root.Element(ns + "header");
For comparison, here is one way to do it with Linq-to-XML:
XDocument doc = XDocument.Load(xmlFileName);
XNamespace ns = "http://www.example.org/genericClientProfile";
var header = doc.Descendants(ns+"header").Single();
H_HEADER header = new H_HEADER();
header.SERVICEID = (string) header.Element(ns + "serviceId");
header.VERSIONID = (double) header.Element(ns + "versionId");
header.BRANDCODE = (string) header.Element(ns + "brandCode");
header.CREATIONTIME = (DateTime) header.Element(ns + "creationTime");
I'm having a heck of a time parsing this layout:
<string xmlns="http://www.namespaceuri.com/Admin/ws">
<CardTrxSummary>
<PaymentMethod>
<Payment_Type_ID>VISA </Payment_Type_ID>
<Authorization>0.0000</Authorization>
<Capture>0.0000</Capture> <ForceCapture>0.0000</ForceCapture>
<PostAuth>0.0000</PostAuth> <Return>0.0000</Return>
<Sale>3419.2700</Sale> <Receipt>0.0000</Receipt>
<RepeatSale>0.0000</RepeatSale>
<Activate>0.0000</Activate>
<Deactivate>0.0000</Deactivate>
<Reload>0.0000</Reload>
<Authorization_Cnt>0</Authorization_Cnt>
<Capture_Cnt>0</Capture_Cnt>
<ForceCapture_Cnt>0</ForceCapture_Cnt>
<PostAuth_Cnt>0</PostAuth_Cnt>
<Return_Cnt>0</Return_Cnt>
<Sale_Cnt>13</Sale_Cnt>
<Receipt_Cnt>0</Receipt_Cnt>
<RepeatSale_Cnt>0</RepeatSale_Cnt>
<Activate_Cnt>0</Activate_Cnt>
<Deactivate_Cnt>0</Deactivate_Cnt>
<Reload_Cnt>0</Reload_Cnt>
<Cnt>13</Cnt>
</PaymentMethod>
</CardTrxSummary>
</string>
I am trying with this code to get the a specific result:
private static string ReadValueFromXml(XmlDocument xmlDocument, string field)
{
var xdoc = xmlDocument.ToXDocument();
var ns = "http://www.namespaceuri.com/Admin/ws";
return xdoc.Descendants(ns + "PaymentMethod")
.Select(x => (string) x.Attribute("Cnt"))
.FirstOrDefault();
}
At this point, it's giving me this message:
The ':' character, hexadecimal value 0x3A, cannot be included in a
name.
I tried it this way:
XmlNodeList xnList = xmlDocument.SelectNodes("/CardTrxSummary/PaymentMethod");
foreach (XmlNode xn in xnList)
{
Console.WriteLine("Sale: " + xn["Sale"].InnerText);
Console.WriteLine("Sale_Cnt: " + xn["Sale_Cnt"].InnerText);
Console.WriteLine("Payment_Type_ID: " + xn["Payment_Type_ID"].InnerText);
}
And it never went inside the foreach.
How do I get the values within PaymentMethod?
EDIT
I looked at the xmldocument's innertext and this is how it's displayed:
"<?xml version=\"1.0\" encoding=\"utf-8\"?><string xmlns=\"http://www.namespaceuri.com/Admin/ws\"><CardTrxSummary>\r\n <PaymentMethod>\r\n <Payment_Type_ID>VISA </Payment_Type_ID>\r\n <Authorization>0.0000</Authorization>\r\n <Capture>0.0000</Capture>\r\n <ForceCapture>0.0000</ForceCapture>\r\n <PostAuth>0.0000</PostAuth>\r\n <Return>0.0000</Return>\r\n <Sale>3419.2700</Sale>\r\n <Receipt>0.0000</Receipt>\r\n <RepeatSale>0.0000</RepeatSale>\r\n <Activate>0.0000</Activate>\r\n <Deactivate>0.0000</Deactivate>\r\n <Reload>0.0000</Reload>\r\n <Authorization_Cnt>0</Authorization_Cnt>\r\n <Capture_Cnt>0</Capture_Cnt>\r\n <ForceCapture_Cnt>0</ForceCapture_Cnt>\r\n <PostAuth_Cnt>0</PostAuth_Cnt>\r\n <Return_Cnt>0</Return_Cnt>\r\n <Sale_Cnt>13</Sale_Cnt>\r\n <Receipt_Cnt>0</Receipt_Cnt>\r\n <RepeatSale_Cnt>0</RepeatSale_Cnt>\r\n <Activate_Cnt>0</Activate_Cnt>\r\n <Deactivate_Cnt>0</Deactivate_Cnt>\r\n <Reload_Cnt>0</Reload_Cnt>\r\n <Cnt>13</Cnt>\r\n </PaymentMethod>\r\n</CardTrxSummary></string>"
Which, I assume, is part of my problem?
EDIT#2
This is what I ended up doing to get it to work. I'm sure there is a better way:
var tst2 = tst.InnerText.Replace("<", "<").Replace(">", ">").Replace("\r\n", string.Empty);
Console.WriteLine("Cnt: " + ReadXmlValue1(tst2, "Cnt"));
and my method to parse it:
private static void ReadXmlValue1(string xmlDocument)
{
XDocument xdoc = XDocument.Parse(xmlDocument);
//XNamespace ns = "http://www.namespaceuri.com/Admin/ws";
var payments = from p in xdoc.Descendants("PaymentMethod")
select new
{
Sale = (decimal)p.Element("Sale"),
SaleCount = (int)p.Element("Sale_Cnt"),
PaymentType = (string)p.Element("Payment_Type_ID")
};
Console.WriteLine("Count: " + payments.Count());
foreach (var payment in payments)
{
Console.WriteLine("Sale: " + payment.Sale);
Console.WriteLine("Sale_Cnt: " + payment.SaleCount);
Console.WriteLine("Payment_Type_ID: " + payment.PaymentType);
}
}
EDIT#3
This is how I'm creating the xmldocument:
/// <summary>
/// Get Data in xml format by url
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private static XmlDocument GetXmlDataFromUrl(string url)
{
//requesting the particular web page
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
//geting the response from the request url
var response = (HttpWebResponse)httpRequest.GetResponse();
//create a stream to hold the contents of the response (in this case it is the contents of the XML file
var receiveStream = response.GetResponseStream();
//creating XML document
var mySourceDoc = new XmlDocument();
//load the file from the stream
if (receiveStream != null)
{
mySourceDoc.Load(receiveStream);
//close the stream
receiveStream.Close();
return mySourceDoc;
}
return null;
}
You can use LINQ to XML to get list of strongly typed anonymous payment objects:
WebClient client = new WebClient();
string content = client.DownloadString(url);
XDocument xdoc = XDocument.Parse(content);
XNamespace ns = "http://www.namespaceuri.com/Admin/ws";
var payments = from p in xdoc.Descendants(ns + "PaymentMethod")
select new {
Sale = (decimal)p.Element(ns + "Sale"),
SaleCount = (int)p.Element(ns + "Sale_Cnt"),
PaymentType = (string)p.Element(ns + "Payment_Type_ID")
};
Keep in mind, that your xml has namespace declared, so you should provide it when specifying element names.
Usage:
foreach(var payment in payments)
{
Console.WriteLine("Sale: " + payment.Sale);
Console.WriteLine("Sale_Cnt: " + payment.SaleCount);
Console.WriteLine("Payment_Type_ID: " + payment.PaymentType);
}
XmlNode node = xmlDocument.SelectSingleNode("/string/CardTrxSummary/PaymentMethod");
Console.WriteLine("Sale: " + node.SelectSingleNode("Sale").InnerText);
Console.WriteLine("Sale_Cnt: " + node.SelectSingleNode("Sale_Cnt").InnerText);
Console.WriteLine("Payment_Type_ID: " + node.SelectSingleNode("Payment_Type_ID").InnerText);
or you can use getElementByTagName instead;
Console.WriteLine("Sale: " + xmlDocument.getElementByTagName("Sale").InnerText);
Console.WriteLine("Sale_Cnt: " + xmlDocument.getElementByTagName("Sale_Cnt").InnerText);
Console.WriteLine("Payment_Type_ID: " + xmlDocument.getElementByTagName("Payment_Type_ID").InnerText);
And just a note, the above 2 method is assuming that the tag will never return null.
If you want to handle possible null node, you can do something like this.
string text = xmlDocument.getElementByTagName("Sale") != null ? xmlDocument.getElementByTagName("Sale").InnerText : "unidentified";
The above line has the format like this:
var variable = condition ? A : B;
It's basically saying that if condition is true, variable equals A, otherwise variable equals B.
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>();