I have an XML file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="publisher" type="KP.Common.Util.XamlConfigurationSection, KP.Common"/>
</configSections>
<publisher>
<p:Publisher xmlns:p="http://schemas.KP.com/xaml/common/notification">
<p:KPLogSubscriber MinimumImportance="Information" />
<p:EventLogSubscriber MinimumImportance="Warning" Source="KPTTY" Log="Application" />
<p:DatabaseMailSubscriber xmlns="http://schemas.KP.com/xaml/data/ef" MinimumImportance="Error" ProfileName = "" Recipients = "administrator#firm.com" Subject = "KPTTY Error" />
</p:Publisher>
</publisher>
</configuration>
I am trying to read the value of the key Recipients using this code:
XmlDocument config = new XmlDocument();
config.Load(configPath);
XmlNode node = config.SelectSingleNode(#"/*[local-name() = 'configuration']/*[local-name() = 'publisher']/*[local-name() = 'Publisher']/*[local-name() = 'DatabaseMailSubscriber']/#Recipients");
Console.WriteLine(node.Value);
but I get an exception (node is null). Is there something wrong with my Xpath? I am trying to ignore any namespaces that might not be present in the xml.
If it is OK to use Linq2Xml
XDocument xDoc = XDocument.Load(fname);
var recipients = xDoc.Descendants()
.First(d => d.Name.LocalName == "DatabaseMailSubscriber")
.Attribute("Recipients")
.Value;
You forgot to do local-name for "Recipients". "Recipients" attribute have empty prefix meaning its namespace is xmlns="http://schemas.KP.com/xaml/data/ef" as defined on the "DatabaseMailSubscriber" element.
I.e. if you don't care of path you can simple use "//" for "any child:
"//*[local-name() = 'DatabaseMailSubscriber']/#*[local-name() = 'Recipients]"
Note: consider actually using namespace properly... or use XDocument as suggested by L.B.
Related
How can I read the content of the childnotes using Xpath?
I have already tried this:
var xml = new XmlDocument();
xml.Load("server-status.xml");
var ns = new XmlNamespaceManager(xml.NameTable);
ns.AddNamespace("ns", "namespace");
var node = xml.SelectSingleNode("descendant::ns:server[ns:ip-address]", ns)
Console.WriteLine(node.InnerXml)
But I only get a string like this:
<ip-address>127.0.0.50</ip-address><name>Server 1</name><group>DATA</group>
How can I get the values individually?
Xml file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<server-status xmlns="namespace">
<server>
<ip-address>127.0.0.50</ip-address>
<name>Server 1</name>
<group>DATA</group>
</server>
</server-status>
You're using XML namespaces in XPath correctly.
However, your original XPath,
descendant::ns:server[ns:ip-address]
says to select all ns:server elements with ns:ip-address children.
If you wish to select the ns:ip-address children themselves, instead use
descendant::ns:server/ns:ip-address
Similarly, you could select ns:name or ns:group elements.
I have a XML File as shown below ,
<?xml version="1.0"?>
<MainClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<Settings xsi:type="FileModel">
<Name>FileOne</Name>
<IsActive>true</IsActive>
<IsHidden>false</IsHidden>
</Settings>
<Settings xsi:type="FileModel">
<Name>FileTwo</Name>
<IsActive>true</IsActive>
<IsHidden>false</IsHidden>
</Settings>
<Settings xsi:type="ServerModel">
<Name>DelRep</Name>
<IsActive>false</IsActive>
<IsHidden>false</IsHidden>
</Settings>
</Items>
<DirectoryPath>D:\MainFolder</DirectoryPath>
</MainClass>
I am extracting some data using the Following code ,
XDocument File = XDocument.Load(path);
XElement element = File .Root.Elements().Single(x => x.Name == "DirectoryPath");
string usingPath = element.Value;
I have been trying to add a certain validation to the above code such that even in a situation that the xml file is missing the part <DirectoryPath>D:\MainFolder</DirectoryPath> , I would not get the error " sequence contains no matching element ".
Is there a property Similar to may be Path.Exist in C# to validate the presence of an XML element
You could use SingleOrDefault, which returns default value if the element is not found
XElement element = File .Root.Elements().SingleOrDefault(x => x.Name == "DirectoryPath");
if(element != null)
{
string usingPath = element.Value;
}
Use: SignleOrDefault. Then you will get proper XElement or null.
There is no default method to verify whether the given node exists or not.But we can use extension methods to achieve the same.
public static class XElementExtension
{
public static bool HasElement(this XElement xElement, string elementName)
{
return xElement.Elements(elementName).Any();
}
}
// Main
var xmlDocument = XElement.Load(#"TestFile.xml", LoadOptions.None);
string elementName = "DirectoryPath";
bool hasElement = xmlDocument.HasElement(elementName);
if(hasElement)
{
Console.WriteLine(xmlDocument.Elements(elementName).First().Value);
}
I have got problems to update a XML Document.
<?xml version="1.0" encoding="utf-16"?>
<test>
<settings server="1" />
</test>
For example I want to update the "1".
I tried like this:
XmlDocument doc = new XmlDocument();
doc.Load(path);
doc.SelectSingleNode("test/settings/server").InnerText = "2"
doc.Save(path);
I think this should be easy to solve, but I am really a blockhead.
UPDATE:
I have tried your solutions and they work with the given example.
Thank you to all of you!
But in the given XML, there is a weird structure, I got problems with:
<?xml version="1.0" encoding="utf-16"?>
<test>
<settings server="1" />
<settings config="999" />
</test>
With this structure none of your solutions work and i always get a "System.NullReferenceException" if I try to change the '999' of config.
I only can access the '1' of server.
Sorry, I didn't expected this and tried to keep the example as easy as possible.
You can do this with
var path = #"C:\users\bassie\desktop\test.xml";
var doc = new XmlDocument();
doc.Load(path);
var settings = doc.SelectSingleNode("test/settings");
settings.Attributes["server"].Value = "2";
doc.Save(path);
InnerText would be used if you wanted to update settings to read something like:
<settings server="1"> 2 </settings>
Where you are trying to update an attribute of the settings element.
Regarding your update, you can replace doc.SelectSingleNode with doc.SelectNodes like so:
var settings = doc.SelectNodes("test/settings");
This will select all the available settings elements under test.
Then when setting the attribute you just provide the index of the element you want to target, e.g.:
settings[0].Attributes["server"].Value = "2";
to update the value of server, or
settings[1].Attributes["config"].Value = "000";
to update the value of config.
However
I think your best best here would be to use System.Xml.Linq, so that you can select the correct settings element by attribute name:
var document = XDocument.Load(path);
var attributeName = "server";
var element = document.Descendants("settings")
.FirstOrDefault(el => el.Attribute(attributeName) != null);
That code gets all settings elements (Descendants) in the document, and then selects the first one where the attributeName ("server" in this case) is not null.
This of course relies on the fact that each attribute only appear once (i.e. you can't have multiple settings elements with the "server" attribute), as it uses the FirstOrDefault selector meaning it will only return 1 element.
Hope this helps
server is an attribute
var doc = new XmlDocument();
doc.Load(path);
doc.SelectSingleNode("test/settings").Attributes["server"].Value = "2"
doc.Save(path);
Try this:
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlNode root = doc.DocumentElement;
XmlNode myNode = root.SelectSingleNode("test/settings");
myNode.Attributes["server"].Value = "2";
doc.Save(path);
Or LINQ to XML
var document = XDocument.Load(path);
document.Descendants("settings").First().Attribute("server").Value = "2";
document.Save(path);
I need to acсess xml file. But xml have base namespace whith prefix m:
This is my code, but it do not working, write NullRefference exeptions:
var fileКс = XDocument.Load(somePath);
var allDescrioptions = fileКс.Root.Element("formulas").Elements("formula").ToList();
This is part of xml file:
<?xml version="1.0" encoding="utf-8" ?>
<m:math xmlns:m="http://www.kontur-extern.ru/ФНС 4.0/math.xsd">
<m:formulas>
<m:formula target="#ПрибУб" match="/Файл/Документ/Прибыль/РасчНал" source="Лист 02/стр.060">
</m:formula>
</m:formulas>
</m:math>
I think need to specify namespace, but I don't know how
You can use XNamespace as follow :
XNamespace m = "http://www.kontur-extern.ru/ФНС 4.0/math.xsd";
var fileКс = XDocument.Load(somePath);
var allDescrioptions = fileКс.Root.Element(m+"formulas").Elements(m+"formula").ToList();
I am trying to parse somewhat standard XML documents that use a schema called MARCXML from various sources.
Here are the first few lines of an example XML file that needs to be handled...
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd">
<marc:record>
<marc:leader>00925njm 22002777a 4500</marc:leader>
and one without namespace prefixes...
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<leader>01142cam 2200301 a 4500</leader>
Key point: in order to get the XPaths to resolve further along in the program I have to go through a regex routine to add the namespaces to the NameTable (which doesn't add them by default). This seems unnecessary to me.
Regex xmlNamespace = new Regex("xmlns:(?<PREFIX>[^=]+)=\"(?<URI>[^\"]+)\"", RegexOptions.Compiled);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlRecord);
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable);
MatchCollection namespaces = xmlNamespace.Matches(xmlRecord);
foreach (Match n in namespaces)
{
nsMgr.AddNamespace(n.Groups["PREFIX"].ToString(), n.Groups["URI"].ToString());
}
The XPath call looks something like this...
XmlNode leaderNode = xmlDoc.SelectSingleNode(".//" + LeaderNode, nsMgr);
Where LeaderNode is a configurable value and would equal "marc:leader" in the first example and "leader" in the second example.
Is there a better, more efficient way to do this? Note: suggestions for solving this using LINQ are welcome, but I would mainly like to know how to solve this using XmlDocument.
EDIT: I took GrayWizardx's advice and now have the following code...
if (LeaderNode.Contains(":"))
{
string prefix = LeaderNode.Substring(0, LeaderNode.IndexOf(':'));
XmlNode root = xmlDoc.FirstChild;
string nameSpace = root.GetNamespaceOfPrefix(prefix);
nsMgr.AddNamespace(prefix, nameSpace);
}
Now there's no more dependency on Regex!
If you know there is going to be a given element in the document (for instance the root element) you could try using GetNamespaceOfPrefix.