How can I display XML data using C# - c#

I am trying to display the id attribute of the channel element called id, the inner text of the display-name tag and the inner text of the icon that sometimes is contained inside the channel element.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tv SYSTEM "xmltv.dtd">
<tv generator-info-name="xmltv.co.uk" source-info-name="xmltv.co.uk">
<channel id="0052a71acac348ff93f5680aa9c125eb">
<display-name>2910</display-name>
</channel>
<channel id="00da025711e82cf319cb488d5988c099">
<display-name>Sony Movies</display-name>
</channel>
<channel id="00dfea977320f17bb419abaa1f079f39">
<display-name>Good Food</display-name>
<icon src="/images/channels/00dfea977320f17bb419abaa1f079f39.png"/>
</channel>
<channel id="018202232e044b504f9dc5263617d496">
<display-name>The Box</display-name>
<icon src="/images/channels/018202232e044b504f9dc5263617d496.png"/>
</channel>
I tried using this code C# code below But the second if give me a error about not referenced to an object.
XmlDocument doc = new XmlDocument();
doc.Load(xmlLocation);
//dispaly the nodes
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
//get the channel
if (node.Name.Equals("channel"))
{
Debug.WriteLine("Channel Name : " + node.ChildNodes[0].Name.ToString()); //or loop through its children as well
//Debug.WriteLine("Channel Name : " + node.AttributeCount.ToString()); //or loop through its children as well
//get the icon element
if(node.ChildNodes[1].Name != null)
Debug.WriteLine("Channel Name : " + node.ChildNodes[1].Name.ToString());
}
}

Although XDocument/XElement and LinQ to XML is the new trend,
to follow your implementation, and adding to it only one feature (using XPATH to query document contents);
Please find the code to fetch channel names and their respective icon source URL's (if exist)
By applying SelectNodes and SelectSingleNode, the API is iterating over the nodes for us.
// Select all the XML elements whose name is "channel"
foreach (XmlNode channelNode in doc.DocumentElement.SelectNodes("channel"))
{
// check if a child element with the name "display-name" exists
XmlNode displayNameNode = channelNode.SelectSingleNode("display-name");
if (displayNameNode != null)
{
// If yes, print the inner text
Debug.WriteLine("Channel Name : " + displayNameNode.InnerText);
}
// then check if the icon node exists
XmlNode iconNode = channelNode.SelectSingleNode("icon");
if (iconNode != null)
{
// and check if it has an attribute with the name "src"
if (iconNode.Attributes["src"] != null)
{
// and if yes, print out its value
Debug.WriteLine(" Icon Src : " + iconNode.Attributes["src"].Value);
}
}
}

First, you need to convert string to XML and load them up in XmlDocument and then use the XPath as shown below. The simple program you can run that in dotnetfiddle.net to check this out.
using System;
using System.Xml;
public class Program
{
public static void Main()
{
string xmlString = "<tv generator-info-name='xmltv.co.uk' source-info-name='xmltv.co.uk'> <channel id='0052a71acac348ff93f5680aa9c125eb'> <display-name>2910</display-name> </channel> <channel id='00da025711e82cf319cb488d5988c099'> <display-name>Sony Movies</display-name> </channel> <channel id='00dfea977320f17bb419abaa1f079f39'> <display-name>Good Food</display-name> <icon src='/images/channels/00dfea977320f17bb419abaa1f079f39.png'/> </channel> <channel id='018202232e044b504f9dc5263617d496'> <display-name>The Box</display-name> <icon src='/images/channels/018202232e044b504f9dc5263617d496.png'/> </channel></tv>";
XmlDocument xmltest = new XmlDocument();
xmltest.LoadXml(xmlString);
XmlNodeList itemNodes = xmltest.SelectNodes("//tv/channel");
foreach(XmlNode itemNode in itemNodes)
{
if (itemNode!= null) {
Console.WriteLine(string.Format("Id:{0}", (itemNode as XmlElement).GetAttribute("id")));
}
}
}
}

Related

C# WPF Unable to write to XML file

I am new to WPF and am attempting to write a child node to an XML file. Here is the file...
<?xml version="1.0" encoding="utf-8"?>
<Sequences>
<LastSavedSequence name="Last Saved Sequence">
<Test name="Measure Battery Current(Stim)" testNumber="5" Vbat="3.7" Frequency="20" PulseWidth="500" Amplitude="1" Resistance="1600" Anode="1" Cathode="2" ActiveDischarge=""/>
<Test name="Measure Batther Current(No Stim)" testNumber="6" Vbat="2.9" Frequency="20" PulseWidth="500" Amplitude="1" Resistance="1600" Anode="1" Cathode="2" ActiveDischarge=""/>
</LastSavedSequence>
<ScottTestSequence name="Scott Test Sequence">
<Test name="VMO Status" testNumber="4" Vbat="3.7" Frequency="20" PulseWidth="1000" Amplitude="6" Resistance="3000" Anode="1" Cathode="2" ActiveDischarge=""/>
<Test name="Measure Battery Current(Stim)" testNumber="5" Vbat="3.7" Frequency="20" PulseWidth="500" Amplitude="1" Resistance="1600" Anode="1" Cathode="2" ActiveDischarge=""/>
<Test name="Measure Batther Current(No Stim)" testNumber="6" Vbat="2.9" Frequency="20" PulseWidth="500" Amplitude="1" Resistance="1600" Anode="1" Cathode="2" ActiveDischarge=""/>
</ScottTestSequence>
</Sequences>
I am attempting to create an XML child block to go within . I used stringBuilder and then am trying to do an attach child and then a .save. XMLData2 is a global list and contains a the child elements that I get in the for each. Here is my code...
public static List<System.Xml.XmlNode> xmlData2 = new List<System.Xml.XmlNode>();
XmlDocument xmlFromOutSideSequenceFile = new XmlDocument();
xmlFromOutSideSequenceFile.Load("c:\\Users/StarkS02/Documents/SavedSequenceFile.xml");
StringBuilder exampleNode = new StringBuilder();
exampleNode.Append("<");
exampleNode.Append(tbSequenceName.Text.ToString().Replace(" ", ""));
exampleNode.Append(" name=");
exampleNode.Append("'");
exampleNode.Append(tbSequenceName.Text);
exampleNode.Append("'");
exampleNode.Append(">");
foreach (XmlNode node in xmlData2)
{
XmlElement child = xmlFromOutSideSequenceFile.CreateElement(string.Empty, node.OuterXml, string.Empty);
exampleNode.Append("</");
exampleNode.Append(tbSequenceName.Text.ToString().Replace(" ", ""));
exampleNode.Append(">");
xmlFromOutSideSequenceFile.AppendChild(exampleNode);
xmlFromOutSideSequenceFile.Save("c:\\Users/StarkS02/Documents/SavedSequenceFile.xml");
I get a compiler error on the .appendChild statement that I cannot convert a stringBuilder to an XML node. This makes sense but I'm not sure how to fix it. Any ideas?
You can create an XML fragment and append to the document.
var xmlFromOutSideSequenceFile = new XmlDocument();
xmlFromOutSideSequenceFile.Load("c:\\Users/StarkS02/Documents/SavedSequenceFile.xml");
See here for more on DocumentFragment
https://msdn.microsoft.com/en-us/library/system.xml.xmldocument.createdocumentfragment(v=vs.110).aspx
var fragment = xmlFromOutSideSequenceFile.CreateDocumentFragment();
fragment.InnerXml = #"<somexml></somexml>";
xmlFromOutSideSequenceFile.DocumentElement.FirstChild.AppendChild(fragment);
See here for more on XMLNode
https://msdn.microsoft.com/en-us/library/system.xml.xmlnode(v=vs.110).aspx
Decide where you want to put it.
.FirstChild
.LastChild
.NextSibling
.ParentNode
.PreviousSibling
Hope this helps! Cheers!

xmlNode not deleting

im unable to delete an xml node from a xml file. Im unable to figure out what is the issue in my code. I have attached my code
class Program
{
static void Main(string[] args)
{
XmlDocument xDoc=new XmlDocument();
xDoc.Load(#"C:\Users\MyUser\Desktop\Family.xml");
//try 1
XmlNode firstNode=xDoc.SelectSingleNode("Apartments/Family[Father='Father1']");
xDoc.LastChild.RemoveChild(firstNode);
xDoc.RemoveAll();
//Try2
XmlNodeList nodeColl = xDoc.GetElementsByTagName("Apartments/Family");
foreach (XmlNode xNode in nodeColl)
{
if (xNode["Father"].InnerText == "Father1")
{
xNode.ParentNode.RemoveChild(xNode);
}
}
// firstNode.ParentNode.RemoveChild(firstNode);
}
}
the Xml document format is
<?xml version="1.0"?>
<Apartments>
<Family>
<Father>Father1</Father>
<Mother>Mother1</Mother>
<Daughter>Daughter1</Daughter>
<Son>Son1</Son>
</Family>
<Family>
<Father>Father2</Father>
<Mother>Mother2</Mother>
<Daughter>Daughter2</Daughter>
<Son>Son2</Son>
</Family>
<Family>
<Father>Father3</Father>
<Mother>Mother3</Mother>
<Daughter>Daughter3</Daughter>
<Son>Son3</Son>
</Family>
<Family>
<Father>Father4</Father>
<Mother>Mother4</Mother>
<Daughter>Daughter4</Daughter>
<Son>Son4</Son>
</Family>
</Apartments>
Please let me know where im wrong.
After the modifications you need to save the changes to file.
XmlDocument xDoc=new XmlDocument();
xDoc.Load(#"C:\Users\MyUser\Desktop\Family.xml");
XmlNodeList nodeColl = xDoc.GetElementsByTagName("Apartments/Family");
foreach (XmlNode xNode in nodeColl)
{
if (xNode["Father"].InnerText == "Father1")
{
xNode.ParentNode.RemoveChild(xNode);
}
}
// save the changes back to file
xDoc.Save(#"C:\Users\MyUser\Desktop\Family.xml");

How to add new child with value in existing XML file in C# using DOM

I have already an XML file whose structure is like this:
<?xml version="1.0" encoding="utf-16"?>
<configuration>
<FilePath>C:\Recordings</FilePath>
<Timer>2</Timer>
</configuration>
I want to to add one more child in <configuration> and want the new structure like shown below. Moreover, before adding I also want to make sure that the new child added is present or not.
<?xml version="1.0" encoding="utf-16"?>
<configuration>
<FilePath>C:\Recordings</FilePath>
<Timer>2</Timer>
<LastSyncTime>Some Value</LastSyncTime>
</configuration>
If <LastSyncTime> is not there, then and only it should be added otherwise not.
I have tried so far as given below:
try
{
XmlDocument doc = new XmlDocument();
doc.Load(newpath);
XmlNodeList nodes = doc.SelectNodes("LastSyncDateTime");
if(nodes.Count == 0) //Means LastSyncDateTime node does not exist
{
XmlNode mynode = doc.CreateNode(XmlNodeType.Text, "LastSyncDateTime",null);
mynode.Value = DateTime.Now.ToString() + "";
doc.AppendChild(mynode);
}
foreach (XmlNode node in nodes)
{
if (node.LastChild != null)
{
LastSyncDateTime = (node.LastChild.InnerText);
Console.WriteLine("Last Date Time is : " + LastSyncDateTime);
}
}
}
catch (Exception ex)
{
//throw ex;
string str = (String.Format("{0} {1}", ex.Message, ex.StackTrace));
Console.WriteLine(str);
}
But I am getting exception. Please suggest me best working solution for this.
Thanks
EDIT : I am getting the following exception:
[System.InvalidOperationException] = {"The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type."}
You should've used XmlNodeType.Element instead of XmlNodeType.Text. And it needs some other fixes as shown below :
.....
XmlNodeList nodes = doc.SelectNodes("//LastSyncDateTime");
if (nodes.Count == 0) //Means LastSyncDateTime node does not exist
{
XmlNode mynode = doc.CreateNode(XmlNodeType.Element, "LastSyncDateTime", null);
//set InnerText instead of Value
mynode.InnerText = DateTime.Now.ToString();
//append to root node (DocumentElement)
doc.DocumentElement.AppendChild(mynode);
}
.....

Parsing URL/web-service

I made a request to a third party API and it gives me the following response in XML.
<?xml version="1.0" ?>
<abc>
<xyz>
<code>-112</code>
<message>No such device</message>
</xyz>
</abc>
I read this using this code.
XmlDocument doc = new XmlDocument();
doc.Load("*** url ***");
XmlNode node = doc.SelectSingleNode("/abc/xyz");
string code = node.SelectSingleNode("code").InnerText;
string msg = node.SelectSingleNode("message").InnerText;
Response.Write("Code: " + code);
Response.Write("Message: "+ msg);
But I get an error on this line:
string code = node.SelectSingleNode("code").InnerText;
Error is:
Object reference not set to an instance of an object.
I changed the first line of your XML file into:
<?xml version="1.0"?>
to make it valid XML. With this change, your code works for me. Without the change, the parser throws an exception.
You can use LINQ to XML (if confortable):
XDocument doc = XDocument.Load(url);
var selectors = (from elements in doc.Elements("abc").Elements("xyz")
select elements).FirstOrDefault();
string code = selectors.Element("code").Value;
string msg = selectors.Element("message").Value;
As you've given it, there doesn't seem to be anything wrong with your code Edit : Your declaration is wrong, as svinja pointed out, and your xml won't even load into the XmlDocument.
However, I'm guessing that your xml is more complicated, and there is at least one namespace involved, which would cause the select to fail.
It isn't pretty, but what you can do is use namespace agnostic xpath to locate your nodes to avoid using a XmlNamespaceManager:
XmlDocument doc = new XmlDocument();
doc.Load("*** url ***");
XmlNode node = doc.SelectSingleNode("/*[local-name()='abc']/*[local-name()='xyz']");
string code = node.SelectSingleNode("*[local-name()='code']").InnerText;
string msg = node.SelectSingleNode("*[local-name()='message']").InnerText;
Response.Write("Code: " + code);
Response.Write("Message: "+ msg);
Edit - Elaboration
Your code works fine if you correct the declaration to <?xml version="1.0"?>
However, if you introduce namespaces into the mix, your code will fail unless you use namespace managers appropriately.
My agnostic xpath above will also parse an xml document like so:
<?xml version="1.0"?>
<abc xmlns="foo">
<xyz xmlns="bar">
<code xmlns="bas">-112</code>
<message xmlns="xyz">No such device</message>
</xyz>
</abc>
<?xml version="1.0">
<abc>
<xyz>
<code>-112</code>
<message> No such device </message>
</xyz>
</abc>
try to set a list:
XmlNodeList nodeList = root.SelectNodes("/abc/xyz");
then read all the nodes and get their text:
foreach(XmlNode node in nodeList)
{
if(node.Name == "code")
{
string code = node.InnerText;
}
else
if(node.Name == "message")
{
string msg = node.InnerText;
}
}
[XmlRoot("abc")]
public class Entity
{
[XmlElement("xyz")]
public SubEntity SubEntity { get; set; }
}
public class SubEntity
{
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("message")]
public string Message { get; set; }
}
And use standart xmlserializer
var xmlSerializer = new XmlSerializer(typeof(Entity));
var result = xmlSerializer.Deserialize(new XmlTextReader("*** url ***"));
Response.Write("Code: " + result.SubEntity.Code);
Response.Write("Message: "+ result.SubEntity.Message);

how to read & write xml file in C# not rely on the tag name?

Thank you very much for reading my question.
the bottom is the sample of my xml file.please refer that.
i did some xml files before, but by "CMarkXml". "IntoElement, OutofElement", is very clear.
but when C#...i was lost..
1: how to read & write my xml file without using the tag name. i see some articles about operation on xml file by c#, but all assumed that known the tag name.
2: if without tag name, it is very difficult or not recommend. then how to read & write my xml file by XmlDocument? (sorry, but no Ling please, i am very faint with that...).
3: my idear is, for the xml file, get out some section, we still could parse the section by xmldocument.
4: for the write/modify the xml file, of course, should contain delete some section, delete some "leaf", change the attributes...
Thank you very much for reading the long question, and any help i will very appreciate. If you have a good sample code but not continent paste them here, could you send it to "erlvde#gmail.com"?
<root>
<a>i belong to a</a>
<b>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
<bb>
....(other <bb>)
</b>
</root>
Read your xml into XmlDocument:
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml("XML HERE");
Access child nodes:
xmlDocument.ChildNodes[1]
But it's also true that it's very error prone
You can also check if you have child nodes at all:
xmlDocument.HasChildNodes
And get number of child nodes:
xmlDocument.ChildNodes.Count
It looks to me like your elements names contain identifiers. If that is the case, and you have control over the XML schema, I would highly recommend changing your XML to contain elements and/or attributes indicating your identifiers and then use the built in XmlSerializer class for serializing to and from XML. It has many modifiers available, such as XmlElement and XmlAttribute among many others, for formatting the output.
Here is a tutorial to get you started.
If possible, change your XML to something like following which would make it far simpler to manipulate...again if changing the schema is a possibility.
<root>
<a>i belong to a</a>
<b>
<bb id="1">
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb id="2">
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
<bb>
</b>
</root>
Edit this edit reflects the changes you made to your XML
Here is a simple console application which will serialize an object to an XML file and then rehydrate it.
Expected XML
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<a>i belong to a</a>
<b>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
</b>
</root>
Simple Console Application Demonstration
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var items = new root
{
a = "i belong to a",
b = new List<bb>
{
new bb
{
bbClassProperty = new List<int>
{
1,
2,
3,
4,
5
}
},
new bb
{
bbClassProperty= new List<int>
{
1,
2,
3,
4,
5
}
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(root));
using (var textWriter = new StreamWriter(#"C:\root.xml"))
{
serializer.Serialize(textWriter, items);
textWriter.Close();
}
using (var stream = new StreamReader(#"C:\root.xml"))
{
var yourObject = serializer.Deserialize(stream);
}
Console.Read();
}
}
#region [Classes]
public class root
{
public string a { get; set; }
public List<bb> b { get; set; }
}
public class bb
{
[XmlElement("bb")]
public List<int> bbClassProperty { get; set; }
}
#endregion
}
Look into the ChildNodes (and similar) properties and methods on your XmlElement object. These will let you iterate over the children of a node and you can then ask that node for its name.
If you have a XmlNode object, you can use XMLNode.FirstChild to get the child, if it has any. You can also use XMLNode.NextSibling to get the next Node of the same parent node.
Why can't you use the names of the nodes? It's the easiest and most common way. Especially if you use XPath or similar.
XPath is also the answer to your second question.
U can use the class XML reader, a simple example is given here.
using System;
using System.Xml;
class Program
{
static void Main()
{
// Create an XML reader for this file.
using (XmlReader reader = XmlReader.Create("perls.xml"))
{
while (reader.Read())
{
// Only detect start elements.
if (reader.IsStartElement())
{
// Get element name and switch on it.
switch (reader.Name)
{
case "perls":
// Detect this element.
Console.WriteLine("Start <perls> element.");
break;
case "article":
// Detect this article element.
Console.WriteLine("Start <article> element.");
// Search for the attribute name on this current node.
string attribute = reader["name"];
if (attribute != null)
{
Console.WriteLine(" Has attribute name: " + attribute);
}
// Next read will contain text.
if (reader.Read())
{
Console.WriteLine(" Text node: " + reader.Value.Trim());
}
break;
}
}
}
}
}
}
The input file text is:
<?xml version="1.0" encoding="utf-8" ?>
<perls>
<article name="backgroundworker">
Example text.
</article>
<article name="threadpool">
More text.
</article>
<article></article>
<article>Final text.</article>
</perls>
Output
Start element.
Start element.
Has attribute name: backgroundworker
Text node: Example text.
Start element.
Has attribute name: threadpool
Text node: More text.
Start element.
Text node:
Start element.
Text node: Final text.enter code here
You can use the following code to if the file does not contain the headers, in the example above.
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
reader = XmlReader.Create(filePath, settings)
Would something like this help?
void Iterate(XmlNode parent) {
//do something with
//parent.Name
//parent.Value
//parent.Attributes
foreach(XmlNode child in parent.ChildNodes) {
Iterate(child);
}
}
XmlDocument document = new XmlDocument();
document.Load(filename);
XmlNode parent = document.DocumentElement;
Iterate(parent);
You could also store it like that (sorry for any syntactical error, didn't run it)
public class Document {
public Element DocumentElement { set; get; }
private void Load(string fileName) {
XmlDocument document = new XmlDocument();
document.Load(fileName);
DocumentElement = new Element(this, null);
DocumentElement.Load(document.DocumentElement);
}
}
public class Element {
public string Name { set; get; }
public string Value { set; get; }
//other attributes
private Document document = null;
private Element parent = null;
public Element Parent { get { return parent; } }
public List<Element> Children { set; get; }
private int order = 0;
public Element(Document document, Element parent) {
Name = "";
Value = "";
Children = new List<LayoutElement>();
this.document = document;
this.parent = parent;
order = parent != null ? parent.Children.Count + 1 : 1;
}
private Element GetSibling(bool left) {
if(parent == null) return null;
int add = left ? -1 : +1;
Element sibling = parent.Children.Find(child => child.order == order + add);
return sibling;
}
public Element GetLeftSibling() {
return GetSibling(true);
}
public Element GetRightSibling() {
return GetSibling(false);
}
public void Load(XmlNode node) {
Name = node.Name;
Value = node.Value;
//other attributes
foreach(XmlNode nodeChild in node.Children) {
Element child = new Element(document, this);
child.Load(nodeChild);
Children.Add(child);
}
}
}
Document document = new Document();
document.Load(fileName);
For changing/deleting right now you could iterate the tree and find elements by name, but since name is not unique, you would affect many elements at once. You could add an unique id in every tag like
<bb id="bb1"/>
Then read it in Load function like
id = ((XmlElement)node).GetAttribute("id");
and use this id to iterate through the tree. Sorry I don't have time right now to provide something more detailed.

Categories