NullReference at XML-Operations - c#

I'm getting a NullReferenceException upon trying to read an attribute of an xml-file - what attribute to read from what element is defined by user-input.
The StackTrace keeps redirecting me to this line (marked)
XmlDocument _XmlDoc = new XmlDocument();
_XmlDoc.Load(_WorkingDir + "Session.xml");
XmlElement _XmlRoot = _XmlDoc.DocumentElement;
XmlNode _Node = _XmlRoot.SelectSingleNode(#"group[#name='" + _Arguments[0] + "']");
XmlAttribute _Attribute = _Node.Attributes[_Arguments[1]]; // NullReferenceException
Where did I miss the point? What Reference is missing here? I can't figure it out...
Edit: The element exists and so does the attribute (including a value)
<?xml version="1.0" encoding="utf-8"?>
<session>
<group name="test1" read="127936" write="98386" />
<group name="test2" read="352" write="-52" />
<group name="test3" read="73" write="24" />
<group name="test4" read="264524" write="646243" />
</session>
Further explanation: The _Arguments[] is a splitted array of the user input. The user e.g. inputs test1_read - that is splitted to _Arguments[0] = "test" and _Arguments[1] = "read"

Would you not be better using the XmlElement.GetAttribute method? This means you can then use the XmlElement.HasAttribute to do a check before you try to access it. This would definitely avoid the NullReference.
Sample
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(_WorkingDir + "Session.xml");
XmlElement xmlRoot = xmlDoc.DocumentElement;
foreach(XmlElement e in xmlRoot.GetElementsByTagName("group"))
{
// this ensures you are safe to try retrieve the attribute
if (e.HasAttribute("name")
{
// write out the value of the attribute
Console.WriteLine(e.GetAttribute("name"));
// or if you need the specific attribute object
// you can do it this way
XmlAttribute attr = e.Attributes["name"];
Console.WriteLine(attr.Value);
}
}
Also I would suggest you have a look at using LinqToXml when parsing Xml documents in .NET.

In absence of the XML file you are parsing, I would guess that maybe in the XPath expression, you need to specify //group instead of simply group.

Related

C# Create XmlElement from string without xmlns=""

I am building an xml document and I have declared the namespace at the very top.
<Root xmlns="http://www.omg.org/space/xtce" xmlns:xtce="http://www.omg.org/space/xtce" ...>
At some arbitrary level below I want to AppendChild with an element created from a string. My goal is to end up with a document that contains that element without the xmlns AT ALL.
This is the closest I have gotten-
string someElementStr = "<SomeElement name="foo"><SubElement name="bar" /></SomeElement>";
XmlDocumentFragment node = doc.CreateDocumentFragment();
node.InnerXml = someElementStr;
someXmlNodeWithinDoc.AppendChild(node);
This code results in-
<SomeElement name="foo" xmlns="">
<SubElement name="bar" />
</SomeElement>
in the final document.
I use a different construct when I do not have to go from a string to XML-
XmlElement elem = doc.CreateElement("SomeElement", "http://www.omg.org/space/xtce");
elem.SetAttribute("name","foo");
someXmlNodeWithinDoc.AppendChild(elem);
and this yields exactly what I want.
<SomeElement name="foo">
</SomeElement>
I would like to do something line this in my current solution
node.setNamespace("http://www.omg.org/space/xtce") then the document would omit xmlns because it is same as root.
Can someone tell me the idiomatic way to build a document with a single namespace use within, where some elements are stored in the model as a string?
This issue is almost identical to mine except the solution has the luxury of only providing the sub element as a string (everything under "new"). I need the entire element.
string xmlRoot = "<Root xmlns=\"http://www.omg.org/space/xtce\"></Root>";
string xmlChild = "<SomeElement name=\"foo\"><SubElement name = \"bar\"/></SomeElement >";
XDocument xDoc = XDocument.Parse(xmlRoot);
XDocument xChild = XDocument.Parse(xmlChild);
xChild.Root.Name = xDoc.Root.GetDefaultNamespace() + xChild.Root.Name.LocalName;
foreach (XElement xChild2 in xChild.Root.Nodes())
{
xChild2.Name = xDoc.Root.GetDefaultNamespace() + xChild2.Name.LocalName;
}
xDoc.Root.Add(xChild.Root);
string xmlFinal = xDoc.ToString();
This is the solution I ended up with. I did not use #shop350 solution because I didn't want to use XDocument,XElement... Thank you for the feedback though!
// create a fragment which I am going to build my element based on text.
XmlDocumentFragment frag = doc.CreateDocumentFragment();
// here I wrap my desired element in another element "dc" for don't care that has the namespace declaration.
string str = "";
str = "<dc xmlns=\"" + xtceNamespace + "\" ><Parameter name=\"paramA\" parameterTypeRef=\"paramAType\"><AliasSet><Alias nameSpace=\"ID\" alias=\"0001\"/></AliasSet></Parameter></dc>";
// set the xml for the fragment (same namespace as doc)
frag.InnerXml = str;
// let someXmlNodeWithinDoc be of type XmlNode which I determined based on XPath search.
// Here I attach the child element "Parameter" to the XmlNode directly effectively dropping the element <dc>
someXmlNodeWithinDoc.AppendChild(frag.FirstChild.FirstChild);

XML: Retrieve a particular value from xml

From the following XML, I want to find a value based on the Employer.
<?xml version="1.0" encoding="UTF-8"?>
<Document>
<Details>
<Employer>Taxes</Employer>
<Adr>
<Strt>Street</Strt>
<Twn>Town</Twn>
</Adr>
</Details>
<DetailsAcct>
<Recd>
<Payroll>
<Id>9</Id>
</Payroll>
</Recd>
<br>
<xy>A</xy>
</br>
</DetailsAcct>
</Document>
the C# code I applied is
detail = root.SelectSingleNode($"//w:Document//w:Employer[contains(text(), 'Taxes']/ancestor::Employer",nsmgr);
But it gives me an invalid token error.
What am I missing?
The error was due to [contains(...], notice closing parentheses is missing. And since you want to return Employer element, no need for ancestor::Employer here :
//w:Document//w:Employer[contains(., 'Taxes')]
If the XML posted resembles structure of the actual XML (except the namespaces), better to use more specific XPath i.e avoid using costly // :
/w:Document/w:Details/w:Employer[contains(., 'Taxes')]
An alternative is to use LINQ to XML.
If the XML is in a string:
string xml = "<xml goes here>";
XDocument document = XDocument.Parse(xml);
XElement element = document.Descendants("Employer").First();
string value = element.Value;
If the XML is in a .xml file:
XDocument document = XDocument.Load("xmlfile.xml");
XElement element = document.Descendants("Employer").First();
string value = element.Value;
You can also find an employer element with a specific value, if that's what you need:
XElement element = document.Descendants("Employer").First(e => e.Value == "Taxes");
Note: this will throw an exception if no element is found with the specified value. If that is not acceptable, then you can replace .First(...) with .FirstOrDefault(...) which will simply return null if no element is found.

Getting nsi:type in xml

So I have a XML File that looks like this
<MyObjectBuilder_Sector xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SectorObjects>
<MyObjectBuilder_EntityBase xsi:type="MyObjectBuilder_CubeGrid">
<EntityId>-8358349049537298307</EntityId>
<LinearVelocity x="0" y="0" z="0" />
</MyObjectBuilder>
</SectorObjects>
</MyObjectBuilder_Sector>
I was curious how in C# how I could retrieve that MyObjectBuilder_CubeGrid from the MyObjectBuilder_EntityBase node. The best i got is this
fileLoc = ofd.FileName;
XmlDocument xdoc = new XmlDocument();
xdoc.Load(fileLoc);
XmlNode typeOfNode = xdoc.SelectSingleNode("MyObjectBuilder_Sector/SectorObjects/MyObjectBuilder_EntityBase").ToString();
Which of course just gets me the node not the xsi:type of the node. I've looked all over for it and can not find an answer.
-Cheers
     Jacob Bender
You can access node's attribute this way :
XmlNode typeOfNode =
xdoc.SelectSingleNode("MyObjectBuilder_Sector/SectorObjects/MyObjectBuilder_EntityBase");
//here typeValue variable will contains "MyObjectBuilder_CubeGrid"
String typeValue = typeOfNode.Attributes["xsi:type"].Value;

Reading XML to get value of a tag c#

I have my XML as
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<recordsDirectory>F:/model_RCCMREC/</recordsDirectory>
<transferDirectory>F:/model_RCCMrecTransfered/</transferDirectory>
<logDirectory>F:/model_RCCMLOG/</logDirectory>
<connectionstring>Data Source=192.168.1.7;Initial Catalog=RCCMdb;User ID=genesys;Password=genesys</connectionstring>
<table>RCCMrec</table>
<DBdestination>
<val1>ANI</val1>
<val2>DNIS</val2>
<val3>Date</val3>
<val4>Time</val4>
<val5>ConnId</val5>
<val6>UUID</val6>
<val7>EmployeeId</val7>
<val8>AgentDN</val8>
</DBdestination>
</configuration>
I need the value of the recordsDirectory tag.
I tried this,
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load("C:/Users/yachna/Desktop/RCCM_TOOL/configRCCM.xml");
string bvalue = xmldoc.SelectSingleNode("recordsDirectory").InnerText.ToString();
But got an error saying
Object reference not set to an instance of an object.
Yes, SelectSingleNode("recordsDirectory") will return null, because you're applying that XPath to the document itself - which doesn't have a recordsDirectory element at the top level, it has a configuration element. You want:
xmldoc.SelectSingleNode("configuration/recordsDirectory")
Or go via the root element:
xmldoc.DocumentElement.SelectSingleNode("recordsDirectory")
(Or you can fetch all descendant elements call recordsDirectory, etc. There are plenty of options here.)
Personally I'd suggest changing to use LINQ to XML if you can, as it's a simpler way of using XML, IMO. It's not too bad in the code you've given so far, but as you do more things with XmlDocument you'll run into it being a bit of a pain - relatively speaking, anyway.
You should also consider separating the "fetching the node" from getting the text, so you can validate that you've found the one you want:
XmlNode node = xmldoc.DocumentElement.SelectSingleNode("recordsDirectory");
if (node != null)
{
// Use it
}
else
{
// No such node. What do you want to do?
}
Try this one in your SelectSingleNode
XmlNode node = doc.SelectSingleNode("/configuration/recordsDirectory");
string s = node.InnerText.ToString();
Hi To read the recordsDirectory tag you need to do :
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load("C:/Users/yachna/Desktop/RCCM_TOOL/configRCCM.xml");
string bvalue = xmldoc.SelectSingleNode("configuration/recordsDirectory").InnerText.ToString();
It will work perfactly

Why does .NET XML append an xlmns attribute to XmlElements I add to a document? Can I stop it?

I am adding XmlElement to an existing document but an extra attribute is being added. Here is the code:
XmlNode manifest = this.getManifestNode ();
XmlElement manifestEntry = _content.CreateElement ("item", _contentPath);
XmlAttribute id = _content.CreateAttribute ("id");
id.Value = "content" + getManifestNodes ().Count;
XmlAttribute href = _content.CreateAttribute ("href");
href.Value = splitPath [splitPath.Length - 1];
XmlAttribute mediaType = _content.CreateAttribute ("media-type");
mediaType.Value = "application/xhtml+xml";
manifestEntry.Attributes.Append (id);
manifestEntry.Attributes.Append (href);
manifestEntry.Attributes.Append (mediaType);
manifest.AppendChild (manifestEntry);
and the resulting XML:
<item id="content3" href="test1.html" media-type="application/xhtml+xml" xmlns="/home/jdphenix/epubtest/test/OEBPS/content.opf" />
Where is the
xmlns="/home/jdphenix/epubtest/test/OEBPS/content.opf"
coming from? The path that it adds is the location of the document on disk, but I'm not adding it in my code (atleast, that I am aware of). Let me know if you need to know more details.
Edit: I modified my code per Filburt's suggestion and changed
XmlElement manifestEntry = _content.CreateElement ("item", _contentPath);
to
XmlElement manifestEntry = _content.CreateElement ("item");
This is a step in the right direction, but produces the following XML:
<item id="content3" href="test1.html" media-type="application/xhtml+xml" xmlns="" />
You're adding this namespace yourself (Line 2):
XmlElement manifestEntry = _content.CreateElement ("item", _contentPath);
See XmlDocument.CreateElement Method (String, String) - the first String parameter is the qualified name of the element you are adding and the second string is the namespace.
Try
XmlElement manifestEntry = _content.CreateElement ("item");
and everything should be fine.

Categories