I make a request to a service and receive an xml response as shown below. However, I'm trying to store the response values in a Dictionary (or store the values returned in variables) and I can't seem to make it work.
Any help would be greatly appreciated.
xml response received:
<?xml version="1.0"?>
<ncresponse NCERRORPLUS="!" BRAND="ABC" PM="CC" currency="GBP" amount="10" STATUS="9" ACCEPTANCE="test123" NCERROR="0" NCSTATUS="0" PAYID="39409494" orderID="92E5CE91">
</ncresponse>
c# code:
try
{
// Write data
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
}
// Get response
Dictionary<string, string> respValues = new Dictionary<string, string>();
try
{
string body = String.Empty;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("iso-8859-1"));
body += reader.ReadToEnd();
}
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(body);
XmlNodeList list = xmlDoc.GetElementsByTagName("ncresponse");
string xmlResponse = list[0].InnerText;
}
catch (WebException wex)
{
throw;
}
Use this:
using System.Xml.Linq; // required namespace for linq-to-xml
/* ... get xml into 'body' string */
XDocument doc = XDocument.Parse(body);
to load the XML file into an XDocument object.
Then, you can use Linq-to-XML to parse the XML and ToDictionary extension method to create a key / value pair for each attribue of the XML:
var output = doc.Element("ncresponse")
.Attributes()
.Select(c => new {
Key = c.Name,
Value = c.Value
})
.ToDictionary(k => k.Key, v => v.Value);
It seems I overcomplicated things (credit goes to #KyleW). This:
var output = doc.Element("ncresponse")
.Attributes()
.ToDictionary(k => k.Name, v => v.Value);
is equivalent to the inital linq query. Select is only necessary only in case some pre-processing of the values placed in the dictionary is required.
Ouput:
[0] = {[NCERRORPLUS, !]}
[1] = {[BRAND, ABC]}
[2] = {[PM, CC]}
... etc
I needed to solve a similar problem, but my issue was a bit more complicated. The XML document could have any number of child collections, and collections within collections. Each node was guaranteed to have a unique name. I used the following code to place it into a dictionary. A recursive function is used to parse each XElement into the dictionary, or parse any child elements it might have.
private static void RecParseXDoc(XElement element, Dictionary<string, string> xDict)
{
if (element.HasElements)
{
foreach (var childElement in element.Elements())
{
RecParseXDoc(childElement, xDict);
}
}
xDict.Add(element.Name.ToString(), element.Value);
}
We can then initiate the parse like so:
var outputDict = new Dictionary<string, string>();
foreach (var element in xDoc.Root.Elements())
{
RecParseXDoc(element, outputDict);
}
If your XML might have duplicate node names, you can do a .TryGetValue prior to adding the element to the dictionary.
I'm leaving this answer here on the chance others with the issue of child elements finds your question.
Related
I am receiving a XML strings from and API, and would like to convert these to C# objects key-value in order to handle these values also apply some asserts.
The messages are of the form string format:
<?xml version="1.0" encoding="UTF-8"?>
<ReportData>
<ProjectName>Test Data</ProjectName>
<Unit>Unit 1</Unit>
<ReportLabel.Cost>394</ReportLabel.Cost>
</ReportData>
After i get the string API response i am trying to deserialize the XMl like this where responseAPI is the xml above:
XmlDocument doc = new XmlDocument();
using (var reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(responseAPI), XmlDictionaryReaderQuotas.Max))
{
XElement xml = XElement.Load(reader);
doc.LoadXml(xml.ToString());
}
At this point i have and xml but i can not get a key-value object.
i am trying
var jsonText = JsonConvert.SerializeXmlNode(doc);
or
serializer = new XmlSerializer(typeof(Object));
using (TextReader reader = new StringReader(responseAPI))
{
var result = (Object)serializer.Deserialize(reader);
}
or
var response = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseAPI);
I am new to with this kind of response in XML and am not sure the way to performing this. It is not as easy as JSON format.
You can use System.Xml.Linq for this task.
Here is one way to create a dictionary from the XML:
static void Main(string[] args)
{
// or use other overload of the Load() method if the XML is not comes from a file stream, uri etc...
var xmldoc = System.Xml.Linq.XDocument.Load("YOUR FILE PATH");
Dictionary<string, string> XMLKeyval = new Dictionary<string, string>();
foreach (var name in xmldoc.Descendants("ReportData").Elements()
.Select(x => new { Name = x.Name, Value = x.Value }).Distinct())
{
XMLKeyval.Add(name.Name.LocalName, name.Value);
}
// Output
foreach (KeyValuePair<string, string> itm in XMLKeyval)
{
Console.WriteLine(itm.Key + ": " + itm.Value);
}
Console.ReadLine();
}
You can not directly convert xml to a dict of key value pair, Instead we need to iterate through each node in xml and add it as a key value pair to a dict object.
please refer Converting an XML-document to a dictionary
I have to process XML response from a 3rd party REST API over which I do not have control. problem is the XML response in not in proper format something like this:
<XML>
<Meta>
<Status>Success</Status>
<Debug/>
</Meta>
<Result>
<abc>
<DivisionID>tttttttttt</DivisionID>
<UserName><![CDATA[ xxx#xxxxx]]></UserName>
<UserFirstName>xxxx</UserFirstName>
<UserLastName>xxxx</UserLastName>
<UserAccountType>xxxxxxx</UserAccountType>
<UserEmail>xxxxx#xxxxx.xom</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</abc>
<def>
<DivisionID/>
<UserName><![CDATA[ xxx#xxxx]]></UserName>
<UserFirstName>yyyy</UserFirstName>
<UserLastName>vvvvvv</UserLastName>
<UserAccountType>uuuuuuuuu</UserAccountType>
<UserEmail>oooo#vvvvvv</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</def>
....contd
</Result>
var requestUri = new Uri(uriString);
HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(requestUri);
var httpresponse = (HttpWebResponse)httprequest.GetResponse();
Person people = new Person();
List<Person> lstPerson = (from _person in xmlDoc.Document.Element("Result").Elements("Result")
select new Person
{
userName = Xdocument.Load(httpresponse.GetResponseStream()).Root.ToString(),
userEmail = _person.Element("UserEmail").Value
}).ToList();
I need to retrieve the node with value "abc" and "def" and store them in UserName which itself is root node, and also to retrieve the values in between them. so how to do this I tried various ways but was unable to to so.
Update
To create a list of your Person classes using element names for userName and the value of the UserEmail sub-element for userEmail, you can do the following:
try
{
// Load the XML from the external site
XDocument xmlDoc;
var requestUri = new Uri(uriString);
HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(requestUri);
using (var httpresponse = (HttpWebResponse)httprequest.GetResponse())
using (var stream = httpresponse.GetResponseStream())
using (var reader = new StreamReader(stream))
{
xmlDoc = XDocument.Load(reader);
}
// Extract the name & email.
var people = xmlDoc
// Get the "Result" node
.Root.Elements(xmlDoc.Root.Name.Namespace + "Result")
// Loop through its elements
.SelectMany(result => result.Elements())
// Deserialize the element name and UserEmail sub-element value as a Person
.Select(element => new Person { userName = element.Name.LocalName, userEmail = element.Element(xmlDoc.Root.Name.Namespace + "UserEmail").ValueSafe() })
.ToList();
// Process or return the list of people
}
catch (Exception ex)
{
// Handle any web exception encountered.
Debug.WriteLine(ex);
// Or rethrow if it can't be handled here
throw;
}
Using the extension method
public static class XObjectExtensions
{
public static string ValueSafe(this XElement element)
{
return element == null ? null : element.Value;
}
}
Original Answer
Your question is unclear. But if you are asking
If I have loaded an XDocument from some XML, is there a way using XmlSerializer to deserialize a portion which is an embedded list when each element of the list has a custom element name?
Then you can do this as follows:
Loading the XDocument
Navigate to the list you want to deserialize.
For each element, remember the element name, then overwrite it with the class name.
Use XElement.CreateReader() to create an XmlReader for just that element, then pass it to an XmlSerializer for deserialization.
I.e.:
// Load the document
var doc = XDocument.Parse(xml);
var people = doc
// Navigate to the list
.Root.Elements("Result")
.SelectMany(r => r.Elements())
// Deserialize each element in the list as a KeyValuePair<string, Person>
.Select(element =>
{
var name = element.Name;
element.Name = typeof(Person).DefaultXmlElementName(); // Overwrite name with name used by serializer.
using (var reader = element.CreateReader())
{
var person = (Person)new XmlSerializer(typeof(Person)).Deserialize(reader);
return new KeyValuePair<string, Person>(name.LocalName, person);
}
})
.ToList();
Given the XML string
string xml = #"<XML>
<Meta>
<Status>Success</Status>
<Debug/>
</Meta>
<Result>
<abc>
<DivisionID>tttttttttt</DivisionID>
<UserName><![CDATA[ xxx#xxxxx]]></UserName>
<UserFirstName>xxxx</UserFirstName>
<UserLastName>xxxx</UserLastName>
<UserAccountType>xxxxxxx</UserAccountType>
<UserEmail>xxxxx#xxxxx.xom</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</abc>
<def>
<DivisionID/>
<UserName><![CDATA[ xxx#xxxx]]></UserName>
<UserFirstName>yyyy</UserFirstName>
<UserLastName>vvvvvv</UserLastName>
<UserAccountType>uuuuuuuuu</UserAccountType>
<UserEmail>oooo#vvvvvv</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</def>
</Result>
</XML>
";
Using the extension method:
public static class XmlSerializationHelper
{
public static string DefaultXmlElementName(this Type type)
{
var xmlType = type.GetCustomAttribute<XmlTypeAttribute>();
if (xmlType != null && !string.IsNullOrEmpty(xmlType.TypeName))
return xmlType.TypeName;
var xmlRoot = type.GetCustomAttribute<XmlRootAttribute>();
if (xmlRoot != null && !string.IsNullOrEmpty(xmlRoot.ElementName))
return xmlRoot.ElementName;
return type.Name;
}
}
suppose I have the following XML string:
<?xml version="1.0" encoding="utf-8" ?>
<items>
<item1>value1</item1>
<item2>value2</item2>
<item3>value3</item3>
<item4>value4</item4>
<item5>value5</item5>
<item6>value6</item6>
</items>
I need to parse it in a generic way as it may be updated later, and I don't need to modify my code accordingly.
So I have tried the following:
public static Dictionary<string, string> Parser(string xmlString)
{
Dictionary<string, string> parserDictionary = new Dictionary<string, string>();
using (StringReader stringReader = new StringReader(xmlString))
using (XmlTextReader reader = new XmlTextReader(stringReader))
{
// Parse the file and display each of the nodes.
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
parserDictionary.Add(reader.Name, reader.ReadString());
break;
}
}
}
return parserDictionary;
}
This code have 2 issues:
It parse the <items> element with null value, I don't need to
parse it
it ignores <item1>
please advise
Why not something like this:
var parserDictionary = XDocument.Create(xmlString)
.Descendants("items")
.Elements()
.Select(elem => new { Name = elem.Name.LocalName, Value = elem.Value })
.ToDictionary(k => k.Name, v => v.Value);
You could probably even do this:
var parserDictionary = XDocument.Create(xmlString)
.Descendants("items")
.Elements()
.ToDictionary(k => k.Name.LocalName, v => v.Value);
If you need to convert XML to an object representation than that's trivially easy
XDocument xDoc = XDocument.Parse(xmlString);
That's really all you need to do. Once you do that, you can query your xDoc with the Elements, Element, Attribute, Attributes and Descendants Properties.
For an example, here's some code that will print all of your values
XDocument xDoc = XDocument.Parse(xmlString);
foreach(XElement e in xDoc.Elements())
{
Console.WriteLine(e.Value);
}
I'm trying to parse XML string into list, result count is always zero.
string result = "";
string address = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
// Create the web request
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
// Get response
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
// Read the whole contents and return as a string
result = reader.ReadToEnd();
}
XDocument doc = XDocument.Parse(result);
var ListCurr = doc.Descendants("Cube").Select(curr => new CurrencyType()
{ Name = curr.Element("currency").Value, Value = float.Parse(curr.Element("rate").Value) }).ToList();
where I'm going wrong.
The problem is that you're looking for elements without a namespace, whereas the XML contains this in the root element:
xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref"
That specifies the default namespace for any element. Also, currency and rate are attributes within the Cube elements - they're not subelements.
So you want something like:
XNamespace ns = "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";
var currencies = doc.Descendants(ns + "Cube")
.Select(c => new CurrencyType {
Name = (string) c.Attribute("currency"),
Value = (decimal) c.Attribute("rate")
})
.ToList();
Note that because I'm casting the currency attribute to string, you'll end up with a null Name property for any currencies which don't specify that attribute. If you want to skip those elements, you can do so with a Where clause either before or after the Select.
Also note that I've changed the type of Value to decimal rather than float - you shouldn't use float for currency-related values. (See this question for more details.)
Additionally, you should consider using XDocument.Load to load the XML:
XDocument doc = XDocument.Load(address);
Then there's no need to create the WebRequest etc yourself.
XDocument doc = XDocument.Parse(result);
XNamespace ns = "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";
var ListCurr = doc.Descendants(ns + "Cube")
.Where(c=>c.Attribute("currency")!=null) //<-- Some "Cube"s do not have currency attr.
.Select(curr => new CurrencyType
{
Name = curr.Attribute("currency").Value,
Value = float.Parse(curr.Attribute("rate").Value)
})
.ToList();
I have a string containing field/value pairs in xml format and want to parse it into Dictionary object.
string param = "<fieldvaluepairs>
<fieldvaluepair><field>name</field><value>books</value></fieldvaluepair>
<fieldvaluepair><field>value</field><value>101 Ways to Love</value></fieldvaluepair>
<fieldvaluepair><field>type</field><value>System.String</value></fieldvaluepair>
</fieldvaluepairs>";
Is there a quick and simple way to read and store all the field/value pairs in Dictionary object? No LINQ please.
Also, there could be possibility that the value field can contain an xml string itself. the solutions provided is wonderful but fails if the value is xml iteself. Example: If the value is something like
<?xml version="1.0" encoding="utf-8"?><GetCashFlow xmlns="MyServices"><inputparam><ac_unq_id>123123110</ac_unq_id></inputparam></GetCashFlow>
It errors out with the message:
Unexpected XML declaration. The XML
declaration must be the first node in
the document, and no white space
characters are allowed to appear
before it.
Please feel free to suggest any modification in xml format if you think it can make things easier to store in Dictionary object.
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><r><a><k>key1</k><v><![CDATA[<?xml version=\"1.0\" encoding=\"utf-8\"?><foo><bar>Hahaha</bar></foo>]]></v></a><a><k>key2</k><v>value2</v></a></r>";
PrintDictionary(XmlToDictionaryLINQ(xml));
PrintDictionary(XMLToDictionaryNoLINQ(xml));
}
private static Dictionary<string, string> XMLToDictionaryNoLINQ(string xml)
{
var doc = new XmlDocument();
doc.LoadXml(xml);
var nodes = doc.SelectNodes("//a");
var result = new Dictionary<string, string>();
foreach (XmlNode node in nodes)
{
result.Add(node["k"].InnerText, node["v"].InnerText);
}
return result;
}
private static Dictionary<string, string> XmlToDictionaryLINQ(string xml)
{
var doc = XDocument.Parse(xml);
var result =
(from node in doc.Descendants("a")
select new { key = node.Element("k").Value, value = node.Element("v").Value })
.ToDictionary(e => e.key, e => e.value);
return result;
}
private static void PrintDictionary(Dictionary<string, string> result)
{
foreach (var i in result)
{
Console.WriteLine("key: {0}, value: {1}", i.Key, i.Value);
}
}
}
}
Getting the data into a dictionary is simple enough using LINQ:
XDocument d = XDocument.Parse(xml);
Dictionary<string, string> dict = d.Descendants("fieldvaluepair")
.Where(x => x.Descendants("field").FirstOrDefault() != null
&& x.Descendants("value").FirstOrDefault() != null)
.ToDictionary(x => x.Descendants("field").First().Value,
x => x.Descendants("value").First().Value);
If you've got a high degree of confidence that your fieldvaluepair elements all have field and value children, you can omit that Where call.
If the value element can contain non-text content, the problem is more complicated. The first question is: do you need to create a Dictionary<string, string>, or a Dictionary<string, IEnumerable<XNode>>? That is, should the value of each dictionary entry contain the inner XML of the value element as a string, or should it contain all of the nodes that are children of the value element?
If it's the former, you need to change the value function to:
x => string.Join("", x.Descendants("value").First()
.Descendants().ToString()
.ToArray()
which concatenates together the string value of all of the descendant nodes. If it's the latter, just do:
x => x.Descendants("value").Descendants()
That said, the case that you describe in your question, where the value element contains the string:
<?xml version="1.0" encoding="utf-8"?><GetCashFlow xmlns="MyServices"><inputparam><ac_unq_id>123123110</ac_unq_id></inputparam></GetCashFlow>
is not well-formed XML. If the process that creates the string you're parsing is producing that, it needs to be fixed so that it uses an XML library to produce XML instead of string concatenation.
I describe a snippet that show how to read a XML content and put it into a Dictionary. Remember that is not the best way to handle it, because this technic is step-by-step, so, if you have a very complex xml structure that maner will be bery difficult to manager. But for your case it can be used as well. I did it without using LINQ maner (better form) to answer your request. I hope you can use it or try to modify to get a better result. Any question post here again.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
namespace XMLReader
{
/// <summary>
/// Author: Inocêncio T. de Oliveira
/// Reader a XML file and put all content into a Dictionary
/// </summary>
class Program
{
static void Main(string[] args)
{
//the source of the XML
XmlTextReader reader = new XmlTextReader("c:\\myxml.xml");
//dictionary to be filled
Dictionary<string, string> dic = new Dictionary<string, string>();
//temporary variable to store the field value
string field = "";
int count = 0;
//reading step thru the XML file
while (reader.Read())
{
XmlNodeType nt = reader.NodeType;
if (nt == XmlNodeType.Text)
{
//Console.WriteLine(reader.Name.ToString());
if (count == 0)
{
//store temporarily the field value
field = reader.Value.ToString();
count++;
} else if (count == 1)
{
//add a new entry in dictionary
dic.Add(field, reader.Value.ToString());
count = 0;
}
}
}
//we done, let´s check if datas are OK stored!
foreach (KeyValuePair<string, string> kvp in dic)
{
Console.WriteLine("field [{0}] : value [{1}]", kvp.Key, kvp.Value);
}
Console.ReadKey();
}
}
}
You can use System.Xml.XmlReader to parse the xml. This works quite ok with a simple structure like in your example. For more complex xml structures, the approach below could turn ugly.
string param = "<fieldvaluepairs><fieldvaluepair><field>name</field><value>books</value></fieldvaluepair><fieldvaluepair><field>value</field><value>101 Ways to Love</value></fieldvaluepair><fieldvaluepair><field>type</field><value>System.String</value></fieldvaluepair></fieldvaluepairs>";
var reader = XmlReader.Create(new StringReader(param));
var dictionary = new Dictionary<string, string>();
reader.ReadStartElement();
while (reader.NodeType != System.Xml.XmlNodeType.EndElement) {
reader.ReadStartElement("fieldvaluepair");
reader.ReadStartElement("field");
string key = reader.ReadString();
reader.ReadEndElement();
reader.ReadStartElement("value");
string value = reader.NodeType == XmlNodeType.Element
? reader.ReadOuterXml() : reader.ReadString();
reader.ReadEndElement();
dictionary.Add(key, value);
reader.ReadEndElement();
}
reader.ReadEndElement();
reader.Close();
EDIT: In the code example above, I'm handling the fact that the value could contain xml as well as text. There is no further parsing of this xml, though, and it is just stored as a string in the dictionary. The same could be done for the 'field' element.