XML To Dictionary - c#

I want the function below to read an XML file and save all the data into the UserClassDict that is passed into the function. The UserClassDict saves the a (username,User Class). The User Class has a property List<int> ControlNumber , where it stores the ControlNumbers.
The XML that it is trying to read looks like this :
<UserClassDictionary>
<adolan>
<ControlNumber>791301</ControlNumber>
</adolan>
<afeazell>
<ControlNumber>790253</ControlNumber>
</afeazell>
<asnyder>
<ControlNumber>790210</ControlNumber>
<ControlNumber>790308</ControlNumber>
</asnyder>
<semery/>
<showard/>
<talexander/>
</UserClassDictionary>
The problem that I'm having is that the LINQ in the function doesn't seem distinguish between the different xml nodes. It doesn't seem to set the node to the Key and the node to the Value.
static void XMLToDictionary(Dictionary<string,User> UserClassDict)
{
XmlDocument doc = new XmlDocument();
doc.Load("UserClassDictionary.xml");
StringWriter sw = new StringWriter();
XmlTextWriter tx = new XmlTextWriter(sw);
doc.WriteTo(tx);
string str = sw.ToString();
XDocument document = XDocument.Parse(str);
foreach (XElement element in document.Descendants().Where(p => p.HasElements == false))
{
int keyInt = 0;
string keyName = element.Name.LocalName;
while (UserClassDict.ContainsKey(keyName))
keyName = element.Name.LocalName + "_" + keyInt++;
UserClassDict.Add(keyName, element.Value);
}
}

First, you don't need a StreamWriter or XmlTextWriter. You can just use XElement.Load() instead.
XElement root = XElement.Load("UserClassDictionary.xml");
Dictionary<string, List<string>> values = new Dictionary<string, List<string>>();
foreach(XElement subNode in root.Elements().Where(x => x.Elements().Count() > 0))
{
values.Add(subNode.Name.LocalName, subNode.Elements("ControlNumber")
.Select(x => x.Value).ToList());
}
Your other reason was you were using Descendents instead of Elements in your loop. This looked for EVERY sub node in your xml, even gradchild nodes.

You can parse your xml and create dictionary of users with Linq to XML:
XDocument xdoc = XDocument.Load("UserClassDictionary.xml");
Dictionary<string, User> users =
xdoc.Root.Elements()
.Select(u => new User {
Name = u.Name.LocalName,
ControlNumber = u.Elements().Select(cn => (int)cn).ToList()
})
.Where(u => ControlNumber.Any())
.ToDictionary(u => u.Name);
Or if you wish to update existing dictionary
var users = from u in xdoc.Root.Elements()
where u.Elements().Any()
select new User {
Name = u.Name.LocalName,
ControlNumber = u.Elements().Select(cn => (int)cn).ToList()
};
foreach(var user in users)
UserClassDict.Add(user.Name, user);

Related

XmlDocument sort nodes by the inner text value of a child

i want to sort the nodes called ImageInfo by the number in the pos node because i have buttons that change the position up or down and i need to sort the ImageInfo node in the correct order when the pos has changed.
i apologise ahead for not having any c# code but i assure you that i have tried so many different things and im in need of help.
here is my xml:
<?xml version="1.0" encoding="utf-8"?>
<MplAndSiImages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MplImages>
<ImageInfo>
<pos>1</pos>
<Name>1.png</Name>
<ParentObjectId>b66a23a8-6268-e611-80e2-c4346bad02e8</ParentObjectId>
<Url>http://localhost:8080/b66a23a8-6268-e611-80e2-c4346bad02e8/1.png</Url>
</ImageInfo>
<ImageInfo>
<pos>2</pos>
<Name>2.png</Name>
<ParentObjectId>b66a23a8-6268-e611-80e2-c4346bad02e8</ParentObjectId>
<Url>http://localhost:8080/b66a23a8-6268-e611-80e2-c4346bad02e8/2.png</Url>
</ImageInfo>
<ImageInfo>
<pos>3</pos>
<Name>3.png</Name>
<ParentObjectId>b66a23a8-6268-e611-80e2-c4346bad02e8</ParentObjectId>
<Url>http://localhost:8080/b66a23a8-6268-e611-80e2-c4346bad02e8/3.png</Url>
</ImageInfo>
</MplImages>
<SiImages />
</MplAndSiImages>
here is my c# code:
it is called on the click of an action link button and i need it to change the poition to 1 less to move it up in the list and i have the number change but the xml need s to be sorted so it has the ImageInfo nodes in the correct order.
public ActionResult MoveUp(string name, string id)
{
var pathConfig = WebConfigurationManager.AppSettings["ProductImageFolderPath"];
var url = pathConfig + id + "\\" + "ModelConfig.xml";
XmlDocument doc = new XmlDocument();
doc.Load(url);
XmlNode root = doc.DocumentElement;
XmlNode upNode = root.SelectSingleNode("/MplAndSiImages/MplImages/ImageInfo[Name/text() = '" + name + "']/pos");
string upNodeValue = upNode.InnerText;
int upNodeInt = Int32.Parse(upNodeValue);
upNodeInt = upNodeInt - 1;
var upNodeString = upNodeInt.ToString();
upNode.InnerText = upNodeString;
XmlNode downNode = root.SelectSingleNode("/MplAndSiImages/MplImages/ImageInfo/pos[text() = '" + upNodeString + "']");
string downNodeValue = downNode.InnerText;
int downNodeInt = Int32.Parse(downNodeValue);
downNodeInt = downNodeInt + 1;
var downNodeString = downNodeInt.ToString();
downNode.InnerText = downNodeString;
Func<string, int> ParseIntOrDefault = (string input) =>
{
int output;
int.TryParse(input, out output);
return output;
};
var result = doc.SelectNodes("MplAndSiImages/MplImages/*")
.Cast<XmlNode>()
.OrderBy(element => element.SelectSingleNode("pos").InnerText)
.ToList();
doc.Save(url);
return RedirectToAction("UploadAnImage", new { id = id });
}
I have seen this and tried it but is there any way of doing this with xmldocument:
XElement root = XElement.Load(xmlfile);
var orderedtabs = root.Elements("Tab")
.OrderBy(xtab => (int)xtab.Element("Order"))
.ToArray();
root.RemoveAll();
foreach(XElement tab in orderedtabs)
root.Add(tab);
root.Save(xmlfile);
I am ordering the images to display on a web page.
and when the move up button is pressed the image will be moved up in the list and swap places with the image above it.
Using linq to xml you can:
var result = XDocument.Load("data.xml")
.Descendants("ImageInfo")
.OrderBy(element => element.Element("pos")?.Value)
.ToList();
And in order to order it by the int value of it you can:
Func<string,int> ParseIntOrDefault = (string input) =>
{
int output;
int.TryParse(input, out output);
return output;
};
var result = XDocument.Load("data.xml")
.Descendants("ImageInfo")
.OrderBy(element => ParseIntOrDefault(element.Element("pos")?.Value))
.ToList();
Using XmlDocument to read the xml you can:
var doc = new XmlDocument();
doc.Load("data.xml");
var result = doc.SelectNodes("MplAndSiImages/MplImages/*")
.Cast<XmlNode>()
.OrderBy(element => element.SelectSingleNode("pos").InnerText)
.ToList();
Here too you can use the ParseIntOrDefault from above

.NET convert three tag into list

I would like to convert a text which contains tag three into a list. Example :
var raw = #"<root><group><tag1>text1</tag1><tag3>text3</tag3</group><tag2>text2</tag2></root>";
And I need to have something like this :
Dictionary<string,string> dicTags = new Dictionary<string,string>();
dicTags["tag1"] = "text1";
dicTags["tag3"] = "text3";
dicTags["tag2"] = "text2";
Here is a example (working but not with this multiple three). The added tag <group></group> make it fail :
var raw = #"<root><group><tag1>text1</tag1><tag3>text3</tag3></group><tag2>text2</tag2></root>";
var doc = XDocument.Parse(raw);
var result = doc.Root.Elements().ToDictionary(e => (string)e.Name.LocalName, e => (string)e);
foreach(var kv in result){
Console.WriteLine("Key: {0}, Value: {1}", kv.Key, kv.Value);
}
After reading your comments I came up with this:
var raw =
#"<root><group><tag1>text1</tag1><tag3>text3</tag3></group><tag2>text2</tag2></root>";
var doc = XDocument.Parse(raw);
var result = doc.Descendants()
.Where(el => !el.HasElements)
.ToDictionary(k => k.Name, v => v.Value);
If you want to ignore tags like <tag4><tag4> (no descendants and empty value) then change the condition to the following:
.Where(el => !el.HasElements && !string.IsNullOrEmpty(el.Value))
Use XMLDocument load your string with LoadXML() method than read your XMLstring.
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xmlstring);
foreach(XmlNode node in doc.DocumentElement.ChildNodes){
string text = node.InnerText;
}

convert data of XML file to string lists C#

This is how the XML file could look:
<data>
<subdata>
<datatype id="1" name="data1">
<xdim>2</xdim>
<ydim>1</ydim>
</datatype>
<datatype id="2" name="data2">
<xdim>3</xdim>
<ydim>4</ydim>
</datatype>
</subdata>
</data>
Now, i want the following:
A list(string) with all datatype id's like "1" & "2" in the preview above
A list(string) with all the < xdim > stuff like "2" & "3" above
A list(string) with all the < ydim > stuff like "1" & "4" above
Are there easy methods built in in C# for stuff like this? Or could anyone help me with this question?
Jonas
You can use Descendents method.
This method reads all the child nodes even the nested ones where node name matches with specified string.
var Idstring = MyXml.Descendants("datatype").Select (x=>x.Attribute("Id")).ToList();
var xdimstring = MyXml.Descendants("xdim").Select (x=>x.Value).ToList();
var ydimstring = MyXml.Descendants("ydim").Select (x=>x.Value).ToList();
To Appease your curiosity :)
This how you can get nodes from specifically subdata node.
var Idstring = MyXml.Descendants("Subdata").Descendants("datatype").Select (x=>x.Attribute("Id")).ToList();
var xdimstring = MyXml.Descendants("Subdata").Descendants("xdim").Select (x=>x.Value).ToList();
var ydimstring = MyXml.Descendants("Subdata").Descendants("ydim").Select (x=>x.Value).ToList();
Now let say you have Multiple subdata and you want to read nodes only from first one...Simply use First linq extension method
var Idstring = MyXml.Descendants("Subdata").First().Descendants("datatype").Select (x=>x.Attribute("Id")).ToList();
This works and is fairly neat and simple:
string xml =
#"<data>
<subdata>
<datatype id=""1"" name=""data1"">
<xdim>2</xdim>
<ydim>1</ydim>
</datatype>
<datatype id=""2"" name=""data2"">
<xdim>3</xdim>
<ydim>4</ydim>
</datatype>
</subdata>
</data>";
var xelem = XElement.Parse(xml);
var allIDs = xelem
.Descendants()
.Where (x => x.Attribute("id") != null)
.Select (x => x.Attribute("id").Value)
.ToList();
var allXdims = xelem
.XPathSelectElements("//xdim")
.Select (x => x.Value)
.ToList();
var allYdims = xelem
.XPathSelectElements("//ydim")
.Select (x => x.Value)
.ToList();
Obviously the part at the start is just getting the XML into an XElement. You might want to do this with:
var xelem = XElement.Load(myXmlLocation);
instead.
The easiest way to convert classes to and from XML in C# is XML Serialization
For your example, you could create a class with member variables corresponding to the tags in your XML. When you want to create your XML file, you serialize the class to an XML File.
When your want to read back the information, you deserialize the contents of the XML file back to the class you created.
Here's a more comprehensive article: https://msdn.microsoft.com/en-us/library/58a18dwa(v=vs.110).aspx
You can use:
XDocument doc = XDocument.Load(#"..\myfile.xml");
to load your file in a XDocument object.
Then use XDocument methods to create string lists of the required id values:
var ids = (from a in doc.Descendants("subdata").Elements().Attributes("id")
select a.Value).ToList();
var xids = (from e in doc.Descendants("datatype").Elements("xdim")
select e.Value).ToList();
var yids = (from e in doc.Descendants("datatype").Elements("ydim")
select e.Value).ToList();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(
"<data><subdata><datatype id=\"1\" name=\"data1\"><xdim>2</xdim><ydim>1</ydim></datatype><datatype id=\"2\" name=\"data2\"><xdim>3</xdim><ydim>4</ydim></datatype></subdata></data>");
var nodes = xmlDocument.SelectNodes("//datatype");
var first = new List<string>();
var Second = new List<string>();
var third = new List<string>();
foreach (XmlNode node in nodes)
{
first.Add(node.Attributes["id"].Value);
}
nodes = xmlDocument.SelectNodes("//xdim");
foreach (XmlNode node in nodes)
{
Second.Add(node.InnerText);
}
nodes = xmlDocument.SelectNodes("//ydim");
foreach (XmlNode node in nodes)
{
third.Add(node.InnerText);
}
Try something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication81
{
class Program
{
static void Main(string[] args)
{
string xml =
"<data>" +
"<subdata>" +
"<datatype id=\"1\" name=\"data1\">" +
"<xdim>2</xdim>" +
"<ydim>1</ydim>" +
"</datatype>" +
"<datatype id=\"2\" name=\"data2\">" +
"<xdim>3</xdim>" +
"<ydim>4</ydim>" +
"</datatype>" +
"</subdata>" +
"</data>";
XElement data = XElement.Parse(xml);
var results = data.Descendants("subdata").Elements()
.GroupBy(x => x.Name.LocalName)
.Select(x => new
{
name = x.Key,
value = x.Select(y => (string)y).ToList(),
attributes = x.Attributes()
.Select(y => new {name = y.Name.LocalName, y.Value})
.GroupBy(y => y.name, z => z.Value)
.ToDictionary(y => y.Key, z => z.ToList())
}).ToList();
}
}
}

How to read a xml request from server into a dictionary key value

I am looking to read the following into a key value dictionary for a web service.
Dictionary<string, string> att = new Dictionary<string, string>();
<Req>
<ID>myProg</ID>
<FID>myID</FID>
<att attName="fName"><Value></Value></att>
<att attName="lName"><Value>Doe</Value></att>
<att attName="Add"><Value>222 Fork Drive</Value></att>
</Req>
I know I can do foreach for the the part where there is actually a attName and value, how can I do that for the first two parts?
Someone said what I have done so far:
void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load("myxmldoc.xml");
var re = doc;
foreach (System.Xml.XmlElement r in re)
{
Console.WriteLine(r);
}
}
Thanks
You can use this code
var root = XElement.Parse(xml);
var att = root.Nodes().Where(n => n.NodeType == XmlNodeType.Element)
.Select(node =>
{
var element = (XElement)node;
return element.Name.LocalName.Equals("att")
? Tuple.Create(element.Attribute("attName").Value, ((XElement)element.FirstNode).Value)
: Tuple.Create(element.Name.LocalName, element.Value);
})
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2);

LINQ to XML Get (children?) of element?

How would I go about getting the ID information using Linq. I'm trying to add them to an array of int.
<FactionAttributes>
<name>Player</name>
<id>0</id>
<relationModifier>1</relationModifier>
<relations>
<id0>100</id0>
<id1>50</id1>
<id2>50</id2>
<id3>50</id3>
<id4>50</id4>
<id5>50</id5>
</relations>
</FactionAttributes>
That is my XML.
Here is the code I'm using so far.
void InitFactions()
{
int count = 0;
string filepath = Application.dataPath + "/Resources/factiondata.xml";
XDocument factionXML = XDocument.Load(filepath);
var factionNames = from factionName in factionXML.Root.Elements("FactionAttributes")
select new {
factionName_XML = (string)factionName.Element("name"),
factionID_XML = (int)factionName.Element("id"),
factionRelations_XML = factionName.Element("relations")// Need to turn this into array.
};
foreach ( var factionName in factionNames)
++count;
foreach ( var factionName in factionNames)
{
Factions f = new Factions();
f.otherFactionsName = new string[count];
f.otherFactionsRelation = new int[count];
int others = 0;
f.FactionName = factionName.factionName_XML;
Debug.Log(factionName.factionRelations_XML);
// Adds Rivals, not self to other list.
foreach (var factionName2 in factionNames)
{
if (factionName.factionID_XML == factionName2.factionID_XML)
continue;
f.otherFactionsName[(int)factionName2.factionID_XML] = factionName2.factionName_XML;
// THIS IS WHERE IM ADDING THE RELATIONS IN //
f.otherFactionsRelation[(int)factionName2.factionID_XML] = factionName.factionRelations_XML[(int)factionName2.factionID_XML];
Debug.Log(f.FactionName + " adds: " + factionName2.factionName_XML);
++others;
}
}
}
I have made multiple attempts using nodes and what not. I can't seem to figure out the correct syntax.
XDocument doc = XDocument.Load(Path);
//To get <id>
var MyIds = doc.Element("FactionAttributes").Element("id").Value;
//To get <id0>, <id1>, etc.
var result = doc.Element("FactionAttributes")
.Element("relations")
.Elements()
.Where(E => E.Name.ToString().Contains("id"))
.Select(E => new { IdName = E.Name, Value = E.Value});
If you want array of ints replace the select with this
.Select(E => Convert.ToInt32(E.Value)).ToArray();
If you are just after the relations Ids use this simple query
var doc = XDocument.Load("c:\\tmp\\test.xml");
var ids = doc.Descendants("relations").Elements().Select(x => x.Value);
If you want the Id and the relations ids in one array use this
var id = doc.Descendants("id").Select(x=>x.Value).Concat(doc.Descendants("relations").Elements().Select(x => x.Value));

Categories

Resources