This is my current attempt to transform key value pair xml to a C# dictionary:
const string xml = #"<KVP><Id>642</Id><StartDate>2012-03-01T00:00:00</StartDate></KVP>";
var xdoc = XDocument.Parse(xml);
if (xdoc.Root != null)
{
var map = xdoc.Root.Elements()
.ToDictionary(a => a.Name.LocalName,
a => a.Value.Trim());
}
Can this be improved?
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;
}
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();
}
}
}
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);
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);