How To Check if an Object is Instantiated? - c#

I'm trying to list down all elements and attributes of an xml into two separate List objects.
I was able to get all the elements in the xml.
But when I tried to add the functionality to get all the attributes within each element, I always encounter System.NullReferenceException: Object reference not set to an instance of an object.
Please review my code below and advise where I'm not doing it right. Or is there any better way to accomplish this? Your comments and suggestions will be highly appreciated.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Xml;
using System.IO;
namespace TestGetElementsAndAttributes
{
public partial class MainForm : Form
{
List<string> _elementsCollection = new List<string>();
List<string> _attributeCollection = new List<string>();
public MainForm()
{
InitializeComponent();
XmlDataDocument xmldoc = new XmlDataDocument();
FileStream fs = new FileStream(#"C:\Test.xml", FileMode.Open, FileAccess.Read);
xmldoc.Load(fs);
XmlNode xmlnode = xmldoc.ChildNodes[1];
AddNode(xmlnode);
}
private void AddNode(XmlNode inXmlNode)
{
try
{
if(inXmlNode.HasChildNodes)
{
foreach (XmlNode childNode in inXmlNode.ChildNodes)
{
foreach(XmlAttribute attrib in childNode.Attributes)
{
_attributeCollection.Add(attrib.Name);
}
AddNode(childNode);
}
}
else
{
_elementsCollection.Add(inXmlNode.ParentNode.Name);
}
}
catch(Exception ex)
{
MessageBox.Show(ex.GetBaseException().ToString());
}
}
}
}
Posting as well the sample XML.
<?xml version="1.0" encoding="UTF-8" ?>
<DocumentName1>
<Product>
<Material_Number>21004903</Material_Number>
<Description lang="EN">LYNX GIFT MUSIC 2012 1X3 UNITS</Description>
<Packaging_Material type="25">457</Packaging_Material>
</Product>
</DocumentName1>

You should check the existence of childNode.Attributes with something like this:
if (childNode.Attributes != null)
{
foreach(XmlAttribute attrib in childNode.Attributes)
{
...
}
}

You need to make sure childNode.Attributes has values, so add if statement prior
if (childNode.Attributes != null)
{
foreach(XmlAttribute attrib in childNode.Attributes)

Related

take data from xml c#

its my xml file:
<?xml version="1.0" encoding="UTF-8"?>
<metadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title>Tytu�1</dc:title>
<dc:creator>autor1</dc:creator>
<dc:subject>Tem1</dc:subject>
<dc:description>O1</dc:description>
<dc:publisher>wydawca1</dc:publisher>
<dc:contributor>wsp�tw�rca1</dc:contributor>
<dc:date>data wydania1</dc:date>
<dc:type>typ zasobu1</dc:type>
<dc:format>format1</dc:format>
<dc:identifier>identyfikator1</dc:identifier>
<dc:source>�r�d�o1</dc:source>
<dc:language>j�zyk1</dc:language>
<dc:relation>powi�zania1</dc:relation>
<dc:coverage>zakres1</dc:coverage>
<dc:rights>prawa1</dc:rights>
</metadata>
Here is my code:
using System;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var dc = "dc";
XDocument doc = XDocument.Load("C:\\Users\\Mateusz\\Desktop\\DublinCore.xml");
var authors = doc.Descendants( dc + "subject");
foreach (var author in authors)
{
Console.WriteLine(author.Value);
}
Console.ReadLine();
}
}
}
I want view in console information about subject.
I think problem is in using namespace.
For example:
<Author>Author</Author> code will work
<dc:Author>Author</Author> code doesn't work
Can someone help me with it?
You need to use XNamespace. So your code becomes:
static void Main(string[] args)
{
XNamespace dc = "http://purl.org/dc/elements/1.1/";
XDocument doc = XDocument.Load("C:\\Users\\Mateusz\\Desktop\\DublinCore.xml");
var authors = doc.Descendants(dc + "subject");
foreach (var author in authors)
{
Console.WriteLine(author.Value);
}
Console.ReadLine();
}
Incidentally it is rather confusing when you use a variable name authors to extract the subject nodes!

XML dialogue tree in Unity not parsing information correctly

I have tried to setup a Dialogue tree within unity using XML (I have not used XML much before so am unsure if the way i am going is correct at all)
So I am trying to get the first text element from this dialogue tree but when i call the XML file and say where it is i am getting the everything stored in that branch.
Am i using the correct .XML to be able to do this also as i seen people say use .XML.LINQ or .XML.Serialization not just .XML is this correct for my case ??
Code:
using UnityEngine;
using System.Collections;
using System.IO;
using System.Xml;
using UnityEngine.UI;
using System.Collections.Generic;
public class DialogTree
{
public string text;
public List<string> dialogText;
public List<DialogTree> nodes;
public void parseXML(string xmlData)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(new StringReader(xmlData));
XmlNode node = xmlDoc.SelectSingleNode("dialoguetree/dialoguebranch");
text = node.InnerXml;
XmlNodeList myNodeList = xmlDoc.SelectNodes("dialoguebranch/dialoguebranch");
foreach (XmlNode node1 in myNodeList)
{
if (node1.InnerXml.Length > 0)
{
DialogTree dialogtreenode = new DialogTree();
dialogtreenode.parseXML(node1.InnerXml);
nodes.Add(dialogtreenode);
}
}
}
}
And here is a picture of the XML.
So i am trying to grab the first element of text then late on there response it will go to branch 1 or 2
<?xml version='1.0'?>
<dialoguetree>
<dialoguebranch>
<text>Testing if the test prints</text>
<dialoguebranch>
<text>Branch 1</text>
<dialoguebranch>
<text>Branch 1a</text>
</dialoguebranch>
<dialoguebranch>
<text>Branch 1b</text>
</dialoguebranch>
</dialoguebranch>
<dialoguebranch>
<text>Branch 2</text>
</dialoguebranch>
</dialoguebranch>
</dialoguetree>
You're getting everything in that branch because XmlNode.InnerXML returns everything in that node. See the documentation for more information on that.
You should use the branch as the base for only looking at its children, instead of starting at xmlDoc every time. Also, you need an entry point to get inside of the first dialoguetree element and then ignore that. Finally, I would only create one XmlDocument and just pass around nodes in your recursion.
Altogether, this might look like this:
public class DialogTree
{
public string text;
public List<DialogTree> nodes = new List<DialogTree>();
public static DialogTree ParseXMLStart(string xmlData)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(new Stringreader(xmlData));
XmlNode rootNode = xmlDoc.SelectSingleNode("dialoguetree/dialoguebranch");
DialogTree dialogTree = new DialogTree();
dialogTree.ParseXML(rootNode);
return dialogTree;
}
public void ParseXML(XmlNode parentNode)
{
XmlNode textNode = parentNode.SelectSingleNode("text");
text = textNode.InnerText;
XmlNodeList myNodeList = parentNode.SelectNodes("dialoguebranch");
foreach (XmlNode curNode in myNodeList)
{
if (curNode.InnerXml.Length > 0)
{
DialogTree dialogTree = new DialogTree();
dialogTree.ParseXML(curNode);
nodes.Add(dialogTree);
}
}
}
}
And you could use it like so:
string xmlStringFromFile;
DialogTree dialogue = DialogTree.ParseXMLStart(xmlStringFromFile);
All of this code is untested but I hope the general idea is clear. Let me know if you find any errors in the comments below and I will try to fix them.

Parse the Nodes of XML files

How to parse all the XML files under a given directory as an input to the application and write its output to a text file.
Note: The XML is not always the same the nodes in the XML can vary and have any number of Child-nodes.
Any help or guidance would be really helpful on this regard :)
XML File Sample
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>
<CNT>USA</CNT>
<CODE>3456</CODE>
</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
</CATALOG>
C# Code
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace XMLTagParser
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please Enter the Location of the file");
// get the location we want to get the sitemaps from
string dirLoc = Console.ReadLine();
// get all the sitemaps
string[] sitemaps = Directory.GetFiles(dirLoc);
StreamWriter sw = new StreamWriter(Application.StartupPath + #"\locs.txt", true);
// loop through each file
foreach (string sitemap in sitemaps)
{
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// cycle through each child noed
foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
{
// first node is the url ... have to go to nexted loc node
foreach (XmlNode locNode in node)
{
string loc = locNode.Name;
// write it to the console so you can see its working
Console.WriteLine(loc + Environment.NewLine);
// write it to the file
sw.Write(loc + Environment.NewLine);
}
}
}
catch {
Console.WriteLine("Error :-(");
}
}
Console.WriteLine("All Done :-)");
Console.ReadLine();
}
}
}
Preferred Output:
CATALOG/CD/TITLE
CATALOG/CD/ARTIST
CATALOG/CD/COUNTRY/CNT
CATALOG/CD/COUNTRY/CODE
CATALOG/CD/COMPANY
CATALOG/CD/PRICE
CATALOG/CD/YEAR
CATALOG/CD/TITLE
CATALOG/CD/ARTIST
CATALOG/CD/COUNTRY
CATALOG/CD/COMPANY
CATALOG/CD/PRICE
CATALOG/CD/YEAR
This is a recursive problem, and what you are looking for is called 'tree traversal'. What this means is that for each child node, you want to look into it's children, then into that node's children (if it has any) and so on, recording the 'path' as you go along, but only printing out the names of the 'leaf' nodes.
You will need a function like this to 'traverse' the tree:
static void traverse(XmlNodeList nodes, string parentPath)
{
foreach (XmlNode node in nodes)
{
string thisPath = parentPath;
if (node.NodeType != XmlNodeType.Text)
{
//Prevent adding "#text" at the end of every chain
thisPath += "/" + node.Name;
}
if (!node.HasChildNodes)
{
//Only print out this path if it is at the end of a chain
Console.WriteLine(thisPath);
}
//Look into the child nodes using this function recursively
traverse(node.ChildNodes, thisPath);
}
}
And then here is how I would add it into your program (within your foreach sitemap loop):
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// start traversing from the children of the root node
var rootNode = xDoc.FirstChild;
traverse(rootNode.ChildNodes, rootNode.Name);
}
catch
{
Console.WriteLine("Error :-(");
}
I made use of this other helpful answer: Traverse a XML using Recursive function
Hope this helps! :)

How can i import MULTIPLE XmlNodes from one document to another?

i am trying to add XmlNodes from one XmlDocument to another as a new node.
My main issue is that i can import only the First or Last child of the document but not the ones in between. all of the nodes i am trying to import have the same layout but i cant seem to create any iteration for importing them all since i can only select either the FirstChild or LastChild - Please note this is also across 2 forms.
I am new to Xml but do not want to re-write my whole Xml Document over again in an XDocument, any help would be greatly appreciated, Thanks.
Code Below:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace XmlCreator
{
public partial class Form_Member : Form
{
string MPlayID, MNick, MName, MMail, MICQ, MRemark;
public static XmlDocument xmlMembers = null;
static XmlNode rootNode = null;
public Form_Member()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, EventArgs e)
{
if (xmlMembers != null)
{
XmlNode member = xmlMembers.CreateElement("member");
XmlAttribute attID = xmlMembers.CreateAttribute("id");
attID.Value = MPlayID;
member.Attributes.Append(attID);
XmlAttribute attNick = xmlMembers.CreateAttribute("nick");
attNick.Value = MNick;
member.Attributes.Append(attNick);
rootNode.AppendChild(member);
XmlNode MNameNode = xmlMembers.CreateElement("name");
MNameNode.InnerText = MName;
member.AppendChild(MNameNode);
XmlNode MMailNode = xmlMembers.CreateElement("email");
MMailNode.InnerText = MMail;
member.AppendChild(MMailNode);
XmlNode MICQNode = xmlMembers.CreateElement("icq");
MICQNode.InnerText = MICQ;
member.AppendChild(MICQNode);
XmlNode MRemarkNode = xmlMembers.CreateElement("remark");
MRemarkNode.InnerText = MRemark;
member.AppendChild(MRemarkNode);
xmlMembers.Save("memberXML.xml");
clearTextFields();
}
}
private void Form_Member_Load(object sender, EventArgs e)
{
xmlMembers = new XmlDocument();
rootNode = xmlMembers.CreateElement("members");
xmlMembers.AppendChild(rootNode);
}
}
}
This is the form of which the Xml file i am trying to import is being created from, i am trying to import this to another form with the following code on the form i am trying to import it to.
code below:
XmlNode memberNode = xmlSquad.ImportNode(Form_Member.xmlMembers.DocumentElement.FirstChild, true);
xmlSquad.DocumentElement.AppendChild(memberNode);
To conclude, it is importing the FirstChild, however, i am making more than 1 memberNode in the xmlMembers.xml file from the other form which i can't find a way of copying over.
Any help will be appreciated, Thank you.
You need the following extension method:
public static class XmlNodeExtensions
{
/// <summary>
/// Copy all child XmlNodes from the source to the destination.
/// </summary>
/// <param name="source">Copy children FROM this XmlNode</param>
/// <param name="destination">Copy children TO this XmlNode</param>
public static void CopyChildren(this XmlNode source, XmlNode destination)
{
if (source == null || destination == null)
throw new ArgumentNullException();
var doc = destination.OwnerDocument;
if (doc == null)
throw new InvalidOperationException("null document");
// Clone the array to prevent infinite loops when the two nodes are from the same document.
foreach (var child in source.ChildNodes.Cast<XmlNode>().ToArray())
{
var copy = doc.ImportNode(child, true);
destination.AppendChild(copy);
}
}
}
You can then use it like:
Form_Member.xmlMembers.DocumentElement.CopyChildren(xmlSquad.DocumentElement);

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