Converting xml tree to json tree - c#

Here's my code which succesfully creates XML:
XDocument xdoc = new XDocument();
XElement root = new XElement("tree");
root.Add(new XAttribute("id", 0));
xdoc.Add(root);
new BuildFoldersTree(root, db);
var items = (from x in db.Items orderby x.name select new { x.name, x.id, x.parent }).ToList();
foreach (var p in items)
{
XElement e = new XElement("item",
new XAttribute("text", p.name),
new XAttribute("id", p.id),
new XAttribute("parentId", p.parent));
XElement parent = root.XPathSelectElement(String.Format("//*[#id=\"FOLDER_{0}\"] ", p.parent.ToString()));
if (parent != null) parent.Add(e);
}
and:
public void BuildFoldersTree(XElement root, MyEntities db)
{
List<Folder> folders = (from x in db.Folders orderby x.parent select x).ToList();
for (int i = 0; i < folders.Count; i++)
{
int f_id = folders[i].parent;
Folder folder = folders[i];
XElement e = new XElement("item",
new XAttribute("text", folder.name),
new XAttribute("id", "FOLDER_" + folder.id.ToString()),
new XAttribute("parentId", folder.parent));
if (folder.parent == 0)
{
root.Add(e);
}
else
{
XElement parent = root.XPathSelectElement(String.Format("//*[#id=\"FOLDER_{0}\"] ", folder.parent.ToString()));
parent.Add(e);
}
}
}
Here's what's going on there:
I have two tables in my database. One for Folders, and one for Items. Each item has a parent folder. The item has a column 'parent' which is integer and which represents the folder id.
But, each folder also has a parent. The folder has a column named 'parent' which is integer and which represents an id of another folder.
With that code, I'm creating an xml tree with folders, and then I add the items to the correct folder.
The previous code works.
Now, I need to make the same algorithm but for Json. So, it won't use Xml, it should create a Json tree.
I have no idea on how to begin. What should I use?
Here's an example of what the result xml looks like:
<tree id="0">
<item text="Folder_name" id="FOLDER_1" parentId="0">
<item text="Other folder name" id="FOLDER_96" parentId="1">
<item text="Third folder name" id="FOLDER_127" parentId="96">
<item text="New folder" id="FOLDER_147" parentId="127" />
<item text="item name" id="959" parentId="147" />
<item text="item name sdgdfh" id="1152" parentId="147" />
</item>
</item>
</item>
</tree>

There is functionality in the JSON.NET library for this. You can use the SerializeXmlNode method of the JsonConvert class contained in the JSON.NET library. Your code would look as follows:
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
string json = JsonConvert.SerializeXmlNode(xmlDocument);
You can find more information here: http://james.newtonking.com/projects/json/help/index.html?topic=html/ConvertingJSONandXML.htm
If we apply this to your example, we have the following:
string xml = "<tree id=\"0\">" +
"<item text=\"Folder_name\" id=\"FOLDER_1\" parentId=\"0\">" +
"<item text=\"Other folder name\" id=\"FOLDER_96\" parentId=\"1\">" +
"<item text=\"Third folder name\" id=\"FOLDER_127\" parentId=\"96\">" +
"<item text=\"New folder\" id=\"FOLDER_147\" parentId=\"127\" />" +
"<item text=\"item name\" id=\"959\" parentId=\"147\" />" +
"<item text=\"item name sdgdfh\" id=\"1152\" parentId=\"147\" />" +
"</item>" +
"</item>" +
"</item>" +
"</tree>";
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
string json = JsonConvert.SerializeXmlNode(xmlDocument);
The json variable now contains the following data:
{
"tree":{
"#id":"0",
"item":{
"#text":"Folder_name",
"#id":"FOLDER_1",
"#parentId":"0",
"item":{
"#text":"Other folder name",
"#id":"FOLDER_96",
"#parentId":"1",
"item":{
"#text":"Third folder name",
"#id":"FOLDER_127",
"#parentId":"96",
"item":[
{
"#text":"New folder",
"#id":"FOLDER_147",
"#parentId":"127"
},
{
"#text":"item name",
"#id":"959",
"#parentId":"147"
},
{
"#text":"item name sdgdfh",
"#id":"1152",
"#parentId":"147"
}
]
}
}
}
}
This is a JSON representation of your XML document.

I think easiest way would be using Json.Net and serializing your XDocument to json.
var jsonstr = JsonConvert.SerializeXNode(xdoc);

Related

c# adding xml element where attribute equals

I have an xml file that has a bunch of channels, and I want to append a channel category to every single one of them. Depending on what channel it is. I'm very new to this so please excuse me if this is an obvious error.
example:
<channel-category>Entertainment</channel-category>
or
<channel-category>News</channel-category>
I have tried the following:
string path;
string xmlfile = "/channels.xml";
path = Environment.CurrentDirectory + xmlfile;
if (exists("channelname1"))
{
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlNode root = doc.DocumentElement;
XmlNode node = root.SelectSingleNode("list/channel[#id='channelname1'");
XmlNode category = doc.CreateElement("channel-category");
category.InnerText = "channelcataegorygoeshere";
node.AppendChild(category);
doc.DocumentElement.AppendChild(node);
}
else
{
Console.WriteLine("not found");
}
Console.ReadKey();
}
static bool exists(string channelname)
{
string path;
string xmlfile = "/channels.xml";
path = Environment.CurrentDirectory + xmlfile;
XDocument xmlDoc = XDocument.Load(path);
bool doesexists = (from data in xmlDoc.Element("list").Elements("channel")
where (string)data.Attribute("id") == channelname
select data).Any();
return doesexists;
}
but it's giving me the following error and I can't figure it out.. What am I doing wrong?
An unhandled exception of type 'System.Xml.XPath.XPathException' occurred in System.Xml.dll
Additional information: 'list/channel[#id='channelname1'' has an invalid token.
from this line
XmlNode node = root.SelectSingleNode("list/channel[#id='channelname1'");
My XML looks like this
<?xml version="1.0" encoding="UTF-8"?>
<list info="list">
<channel id="channelname1">
<display-name lang="en">channelname1</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname2">
<display-name lang="en">channelname2</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname3">
<display-name lang="en">channelname3</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname4">
<display-name lang="en">channelname4</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
</list>
You dont have closing bracket in list/channel[#id='channelname1'(HERE).
Moreover, you are actually trying to do following:
var doc = new XmlDocument();
doc.Load(Environment.CurrentDirectory + "\\channels.xml");
var nodes = doc.SelectNodes("list/channel[#id=\"channelname1\"]");
if (nodes != null)
{
foreach (XmlNode node in nodes)
{
var el = doc.CreateElement("channel-category");
el.InnerText = "SomeValue";
node.AppendChild(el);
}
}
bool doesexists = (from data in xmlDoc.Element("tv").Elements("channel")
where (string)data.Attribute("id") == channelname
select data).Any();
You are trying to reach the channel node where the id equals the channelname inside tv. The problem is that tv does not exist, the channels are inside this:
<list info="list">
Solution: either put the channels into tv, or use a selector appropriate to your current structure.
Why you are using tv instead of list thats why xml library not getting your path of your elements and throwing this error.
try this..
XmlNode node = root.SelectSingleNode("list/channel");
node.Attributes["id"].Value=="channelname1"?true:false;

simple way to read xml data using linq

I have a xml structure like this. Can anyone help with a simple linq function to read this xml structure.The itemEntry node repeats according to data. I tried to read the xml using the method below,but i am getting no records in the list. Is this method here correct way to get the details...
List<CX_ITEMLIST> sList =
(from e in XDocument.Load(param.FileName).Root.Elements("itemEntry")
select new CX_ITEMLIST
{
TITLE = (string)e.Element("title"),
YEAR = (string)e.Element("year"),
ITEMNAME = (string)e.Element("itemname"),
CATRYLIST =
(
from p in e.Elements("categorylist").Elements("categories")
select new CATLIST
{
IDTYPE = (string)p.Element("categoryid"),
IDNUMBER = (string)p.Element("categoryName")
}).ToList()
}).ToList();
<itemslist>
<itemInformation>
<itemdate>01/23/2014</itemdate>
<itemcount>57</itemcount>
</itemInformation>
<itemEntry>
<title>Title1</title>
<year>2013</title>
<itemname>testname</itemname>
<categorylist>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
</categorylist>
</itemEntry>
<itemEntry>
<title>Title1</title>
<year>2013</title>
<itemname>testname</itemname>
<categorylist>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
</categorylist>
</itemEntry>
</itemslist>
You should try with XDocument.
XDocument xdoc = XDocument.Load("file.xml");
The System.Xml.XLinq namespace contains some awesome objects to make this easy.
var xDoc = XDocument.Parse(xml); // load your xml string, or use XDocument.Load() to load an xml file
var itemEntries = xDoc
.Root // refers to itemEntries node
.Descendants("itemEntry"); // gets all itemEntry nodes in an IEnumerable object
This gets you an IEnumerable<XNode> of all the itemEntry nodes.
From there you can do what you need, save the values to a business object, etc.
The above method works properly, i found the issue, my xml tag was having namespace attribute. i tried to get the namespace and append it with Elements while reading
XNamespace ns = xDocument.Root.Attribute("xmlns").Value;
List<CX_ITEMLIST> sList =
(from e in XDocument.Load(param.FileName).Root.Elements(ns + "itemEntry")
select new CX_ITEMLIST
{
TITLE = (string)e.Element(ns + "title"),
YEAR = (string)e.Element(ns + "year"),
ITEMNAME = (string)e.Element(ns + "itemname"),
CATRYLIST =
(
from p in e.Elements(ns + "categorylist").Elements(ns + "categories")
select new CATLIST
{
IDTYPE = (string)p.Element(ns + "categoryid"),
IDNUMBER = (string)p.Element(ns + "categoryName")
}).ToList()
}).ToList();

Adding child nodes using c# Xdocument class

I have an xml file as given below.
<?xml version="1.0" encoding="utf-8"?>
<file:Situattion xmlns:file="test">
<file:Properties>
</file:Situattion>
I would like to add the child element file:Character using xDocument.So that my final xml would be like given below
<?xml version="1.0" encoding="utf-8"?>
<file:Situattion xmlns:file="test">
<file:Characters>
<file:Character file:ID="File0">
<file:Value>value0</file:Value>
<file:Description>
Description0
</file:Description>
</file:Character>
<file:Character file:ID="File1">
<file:Value>value1</file:Value>
<file:Description>
Description1
</file:Description>
</file:Character>
</file:Characters>
Code in c# i tried using Xdocument class is given below.
XNamespace ns = "test";
Document = XDocument.Load(Folderpath + "\\File.test");
if (Document.Descendants(ns + "Characters") != null)
{
Document.Add(new XElement(ns + "Character"));
}
Document.Save(Folderpath + "\\File.test");
At line "Document.Add(new XElement(ns + "Character"));", I am getting an error:
"This operation would create an incorrectly structured document.".
How can I add the node under "file:Characters".
You're trying to add an extra file:Character element directly into the root. You don't want to do that - you want to add it under the file:Characters element, presumably.
Also note that Descendants() will never return null - it will return an empty sequence if there are no matching elements. So you want:
var ns = "test";
var file = Path.Combine(folderPath, "File.test");
var doc = XDocument.Load(file);
// Or var characters = document.Root.Element(ns + "Characters")
var characters = document.Descendants(ns + "Characters").FirstOrDefault();
if (characters != null)
{
characters.Add(new XElement(ns + "Character");
doc.Save(file);
}
Note that I've used more conventional naming, Path.Combine, and also moved the Save call so that you'll only end up saving if you've actually made a change to the document.
Document.Root.Element("Characters").Add(new XElement("Character", new XAttribute("ID", "File0"), new XElement("Value", "value0"), new XElement("Description")),
new XElement("Character", new XAttribute("ID", "File1"), new XElement("Value", "value1"), new XElement("Description")));
Note: I have not included the namespace for brevity. You have to add those.

linq to xml not returning any results possible namespace issue?

Here are the first few lines of my XML document:
<?xml-stylesheet type="text/xsl" href="/3.0/style/exchange.xsl"?>
<ops:world-patent-data xmlns="http://www.epo.org/exchange" xmlns:ops="http://ops.epo.org" xmlns:xlink="http://www.w3.org/1999/xlink">
<ops:meta name="elapsed-time" value="83" />
<exchange-documents>
<exchange-document system="ops.epo.org" family-id="34916979" country="EP" doc-number="1726228" kind="A1">
<bibliographic-data>
<publication-reference>
<document-id document-id-type="docdb">
<country>EP</country>
<doc-number>1726228</doc-number>`
I am trying to extract the doc-number using the code below:
public class biblio
{
public string appNumber { get; set; }
}
XElement xDoc = XElement.Load(#"pathToMyXml.xml");
XNamespace xn = "http://ops.epo.org";
var bib = from exchange in xDoc.Descendants(xn + "exchange-document")
where exchange.Attribute("kind").Equals("A1")
select new biblio
{
appNumber = exchange.Element("doc-number").Value
};
However this does not return any results.
Where am I going wrong?
Thanks.
The namespace of doc-number is http://www.epo.org/exchange. It has been inherited from the root node. You need to specify that in your query. Furthermore, doc-number isn't an element - i.e. direct child - of exchange-document. It is a descendant.
XNamespace d = "http://www.epo.org/exchange";
var bib = from exchange in xDoc.Descendants(xn + "exchange-document")
where (string)exchange.Attribute("kind") == "A1"
select new biblio
{
appNumber = (string)exchange.Descendant(d + "doc-number")
};
Please note that I changed exchange.Attribute("kind").Equals("A1") to (string)exchange.Attribute("kind") == "A1" and exchange.Descendant(d + "doc-number").Value to (string)exchange.Descendant(d + "doc-number").
That prevents NullReferenceExceptions if the attribute or descendant doesn't exist.

C# XML How to retrive innerText of field by attribute?

Here is the XML sample:
<?xml version="1.0" ?>
<XMLScreen xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CX>80</CX>
<CY>24</CY>
<Formatted>true</Formatted>
<Field>
<Location position="1" left="1" top="0" length="69" />
<Attributes Base="226" Protected="false" FieldType="High" />
*SDC SCHEDULING CATEGORY UPDATE
</Field>
</XMLScreen>
I want to retrive the Inner text of each field based on its Location position.
What I have so far is:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myEm.CurrentScreenXML.GetXMLText());
XmlNodeList fields = xmlDoc.GetElementsByTagName("Field");
MessageBox.Show("Field spot: " + i + " Contains: " + fields[i].InnerText);
And I want to be able to just extract the inner text of the field by passing in a number of the location position. So if I say foo[i] I want to be able to get the innertext
*SDC SCHEDULING CATEGORY UPDATE
You should use a xpath search query :
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
int nodeId = 4;
XmlNode node = xmlDoc.SelectSingleNode(String.Format(#"//Location[#position='{0}']", nodeId));
if (node != null)
{
String field = node.ParentNode.InnerText;
}
Something like that, with XDocument instead of XmlDocument (well, if you're not in .net 3.5 or higher, we'll have a problem).
private string GetTextByLocationId(XDocument document, int id)
{
var field = document.Descendants("Field").FirstOrDefault(m => m.Element("Location").Attribute("position").Value == id.ToString());
if (field == null) return null;
return field.Value;
}
and usage
var xDocument = XDocument.Load(<pathToXmlFile or XmlReader or string or ...>);
var result = GetTextByLocationId(xDocument, 1);
EDIT
or if you want a dictionary with :key = position / value = text
private static Dictionary<int, string> ParseLocationAndText(XDocument document)
{
var fields = document.Descendants("Field");
return fields.ToDictionary(
f => Convert.ToInt32(f.Element("Location").Attribute("position").Value),
f => f.Value);
}
Try,
XElement root = XElement.Parse(myEm.CurrentScreenXML.GetXMLText());
XElement field = root.XPathSelectElement(
string.Format("Field[Location/#position='{0}']", 1));
string text = field.Value;
You will need to use the following using to use XPath with XElements.
using System.Xml.XPath;

Categories

Resources