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();
}
}
}
Related
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;
}
I have not used XML for very long and need to extract the useful information from an XML response. If there are 2 tags that are the same but have a different name e.g
<lst name = "stack">
<str>Ola</str>
<lst name = "overflow">
<str>Hello</str>
</lst>
</lst>
How would I extract the contents of the tag with name="overflow"?
You can use LINQ To XML:
var result = XDocument.Parse(xml)
.Descendants("lst")
.Where(e => (string) e.Attribute("name") == "overflow")
.Descendants("str")
.Select(x => x.Value)
.FirstOrDefault();
Try this to start:
XPathDocument docNav = new XPathDocument(pathName);
XPathNavigator nav = docNav.CreateNavigator();
XmlNamespaceManager ns = new XmlNamespaceManager(nav.NameTable);
string val = nav.SelectSingleNode(#"/lst/lst[#name='overflow']/str")
These are good resources for simple XPath navigation and .NET XML Parsing:
http://www.w3schools.com/xpath/
http://www.codeproject.com/Articles/52079/Using-XPathNavigator-in-C
You may use the System.Xml.Linq namespace:
var xDoc = XDocument.Parse(xml);
var result = xDoc.Descendants()
.Where(d =>
d.Name == "lst" &&
d.Attributes("name").FirstOrDefault()!=null &&
d.Attributes("name").FirstOrDefault().Value == "overflow")
.FirstOrDefault();
User Linq to xml
var xmlFile = XDocument.Load(someFile);
var query = from item in xmlFile.Descendants("childobject")
where !String.IsNullOrEmpty(item.Attribute("using")
select new
{
AttributeValue = item.Attribute("using").Value
};
You can do it with LINQ to XML:
var doc = XDocument.Load("YourXMLPath.xml");
var content = doc
.Element("lst")
.Elements("lst")
.Where(e=>((string) e.Attribute("name") ?? "")=="overflow")
.Select(e=>e.Element("str").InnerText())
.FirstOrDefault();
LINQ to XML in System.Xml.Linq namespace.
const string xml = #"<lst name = ""stack""><str>Ola</str><lst name = ""overflow""><str>Hello</str></lst></lst>";
XDocument doc = XDocument.Parse(xml);
IEnumerable<XElement> overflow = doc.Root.Elements("lst").Where(x => (string) x.Attribute("name") == "overflow");
XElement firstOverflow = overflow.FirstOrDefault();
string value = firstOverflow.Descendants("str").FirstOrDefault(x => x.Value);
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);
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));
I'm having an xml file like
<Root>
<Child val1="1" val2="2"/>
<Child val1="1" val2="3"/>
<Child val1="2" val2="4"/>
</Root>
i need to display the data from the Xml file to a Listview like
(Added A to index value)
Now i'm using like
1.Stores the data in an XmlNodesList
2.Then iterate through the nodeslist and add the attribute value to the list view
Here i can not use Dictionary<String,String> as a temporary storage because there exist multiple keys with same name.
Is there any idea to do this using LINQ to XML.?
Without LINQ:
var doc = new System.Xml.XmlDocument();
doc.LoadXml(xml);
var nodes = doc.SelectNodes("Root/Child");
for (int i = 0; i < nodes.Count; i++)
{
var n = nodes[i];
var index = String.Format("A{0}", i + 1);
var column1 = n.Attributes["val1"].Value;
var column2 = n.Attributes["val1"].Value;
// use variables to add an item to ListView
}
Using LINQ:
using System.Linq;
var doc = new System.Xml.XmlDocument();
doc.LoadXml(xml);
var nodes = doc.SelectNodes("Root/Child");
var arr = nodes
.OfType<XmlNode>()
.ToArray();
var result = arr
.Select(n =>
new
{
ClNo = String.Format("A{0}", Array.IndexOf(arr, n) +1),
Val1 = n.Attributes["val1"].Value,
Val2 = n.Attributes["val2"].Value,
});
ListView list = new ListView();
ListViewItem[] items = result
.Select(r => new ListViewItem(new[] { r.ClNo, r.Val1, r.Val2 })
.ToArray();
list.Items.AddRange(items);