1

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.

Sri Reddy
  • 6,832
  • 20
  • 70
  • 112
  • Not an exact duplicate, but this should give you the answer: http://stackoverflow.com/questions/1593235/linq-to-xml-dictionary-conversion – Ocelot20 Feb 17 '11 at 18:06
  • thanks Ocelot20. can that solution be without linq? if so, how? – Sri Reddy Feb 17 '11 at 18:11
  • @user465876 If the `` element contains XML as such, including an XML declaration, then your document is **not well-formed** and it cannot be processed as XML. – jasso Feb 18 '11 at 21:27

4 Answers4

5
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);
            }
        }
    }
}
Jimmy Chandra
  • 6,472
  • 4
  • 26
  • 38
  • thanks jimmy.. same question how to handle xmldata with in value field? – Sri Reddy Feb 17 '11 at 19:47
  • Explain handle... what do you want to do w/ the inner XML? are you trying to create a nested Dictionary where object can be another Dictionary or ... are you just storing the XML as string in the key value pair value property? If the later, as long you put the inner XML inside CDATA section, you should be okay. If you are talking about the other scenario, now it's a bit different... Why bother converting this to Dictionary then, why not just leave it as XElement or XmlDocument object and parse it a different way? – Jimmy Chandra Feb 17 '11 at 20:49
  • Jimmy, the node can have xmldata with which I just intend to store as string. I don't want to loop through the xmldata. – Sri Reddy Feb 17 '11 at 21:07
  • Hmm... like I said if you put it inside CDATA you should be okay, you can use it just like I wrote above. I edited the example to show this... – Jimmy Chandra Feb 18 '11 at 17:39
2

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.

Robert Rossney
  • 94,622
  • 24
  • 146
  • 218
  • Rob, thanks for the wonderful solution. But at this time we are not yet on LINQ. I needed something without LINQ and can process the xml within as normal string. – Sri Reddy Feb 22 '11 at 13:48
1

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();
    }
}
}
Ito
  • 2,167
  • 3
  • 23
  • 32
0

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.

Andreas Vendel
  • 716
  • 6
  • 14
  • @user465876 Then you would probably want to use an XmlSerializer (see http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx). You could replace ReadString with ReadOuterXml and deserialize the return value using an XmlSerializer. – Andreas Vendel Feb 17 '11 at 20:12
  • @user465876 If you're only interested in getting the content of the value element as a string in a dictionary, my code example will work after I edited it. Note, however, that it won't work with an xml declaration inside it ( – Andreas Vendel Feb 17 '11 at 20:29
  • Andreas, I really can't get rid of . I need it in dictionary object as is. Is there any alternative? May be I need to remove that declaration and pass the value to this method and then add it again in dictionary object. Isn't it overhead? – Sri Reddy Feb 17 '11 at 21:15