parsing an xmldocument from a webrequest - c#

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.

Related

Read XML File Using Linq is not reading element

I am not able to get the value from this xml response, I will appreciate any help.
<Response>
<Result>
<Item1>GREEN</Item1>
<Item2>05/19/2017 22:08:14</Item2>
</Result>
<Other>
<Id>xxxxxxxxxxxxc</Id>
</Other>
</Response>
What I tried so far but the results is empty
string responseXml = response.ToXML();
XElement doc = XElement.Load(new StringReader(responseXml));
var results = from p in
doc.Descendants("Result")
select new
{
item = p.Element("Item1").Value,
};
foreach (var elm in results)
{
Console.WriteLine(elm.item);
}
Use Parse instead of load. You may also be getting error due to extra characters in the string. In the string you postged there are single quotes. Not sure if the single quote is in the actual string you are using.
string responseXml = "<Response>" +
"<Result>" +
"<Item1>GREEN</Item1>" +
"<Item2>05/19/2017 22:08:14</Item2>" +
"</Result>" +
"<Other>" +
"<Id>xxxxxxxxxxxxc</Id>" +
"</Other>" +
"</Response>";
XElement doc = XElement.Parse(responseXml);
var results = from p in
doc.Descendants("Result")
select new
{
item = p.Element("Item1").Value,
};
foreach (var elm in results)
{
Console.WriteLine(elm.item);
}

Split XML file in C#

I have an XML file which have multiple messages in one large file, my objective it to split the file into singe xml file for each message, I have a c# code which only gets me the first instance of the message. can you please tell what am I missing here:
Here is my code :
string strSeq;
string strFileName;
XDocument doc = XDocument.Load(#"C:\XMl\MR.xml");
var newDocs = doc.Descendants("Message")
.Select(d => new XDocument(new XElement("FileDump", d)));
foreach (var newDoc in newDocs)
{
strSeq = XDocument.Load(#"C:\XMl\MR.xml").XPathSelectElement
"//FileDump/Message/MsgID").Value;
strFileName = "MR_" + strSeq + ".xml";
newDoc.Save(Console.Out); Console.WriteLine();
newDoc.Save(#"C:\xml\MR\Tst\" + strFileName);
Console.WriteLine();
}
You should search for message ID within newDoc instead of doc:
foreach (var newDoc in newDocs)
{
strSeq = newDoc.XPathSelectElement("//FileDump/Message/MsgID").Value;
strFileName = "MR_" + strSeq + ".xml";
newDoc.Save(Console.Out); Console.WriteLine();
newDoc.Save(#"C:\xml\MR\Tst\" + strFileName);
Console.WriteLine();
}
Try,
string path = #"C:\xml\MR\Tst\MR_";
XElement root = XElement.Load(file);
foreach(XElement message in root.Descendants("Message"))
{
string id = message.Element("MsgID").Value;
message.Save(path + id + ".xml");
}

Entity to XML (EF5.0)

I'm building and XML file representing and Entity. After many hours this seems to be working, but is there a better way ?
var entityContents = (from p in context.people select p).ToListAsEnumerable();
var XmlString = CollectMemebersNameValue("people" , entityContents);
public static string CollectMemebersNameValue( string entityName, IEnumerable entityQuery)
{
var xmlText = new StringBuilder();
xmlText.AppendLine("<" + entityName + ">");
foreach (var item in entityQuery)
{
xmlText.AppendLine("<Row>");
foreach (var prop in item.GetType().GetProperties())
{
if ( ! prop.PropertyType.Name.Contains("ICollection"))
{
var nname = prop.Name;
var nvalue = prop.GetValue(item, null);
xmlText.AppendLine("<" + nname + ">" + nvalue + "</" + nname + ">");
}
}
}
xmlText.AppendLine("</" + entityName + ">");
return xmlText.ToString();
}
Yes, you can use the XmlSerializer, e.g.
XmlSerializer xs = new XmlSerializer(typeof(YourObjectType));
MemoryStream ms = new MemoryStream();
xs.Serialize(ms, yourActualObject);
string sampleXml = Encoding.UTF8.GetString(ms.ToArray());
Whether you're serializing your entity or a view of your entity, it works the same way. Just ensure whatever object you are serializing is serializable.

getting a specific value using linq to xml

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")
};

LINQ to XML without knowing the nodes

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>();

Categories