How can I add xml attributes with different prefixes/namespaces in C#? - c#

I need to be able to create an XML Document that looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rootprefix:rootname
noPrefix="attribute with no prefix"
firstprefix:attrOne="first atrribute"
secondprefix:attrTwo="second atrribute with different prefix">
...other elements...
</rootprefix:rootname>
Here's my code:
XmlDocument doc = new XmlDocument();
XmlDeclaration declaration = doc.CreateXmlDeclaration("1.0", "UTF-8", "yes");
doc.AppendChild(declaration);
XmlElement root = doc.CreateElement("rootprefix:rootname", nameSpaceURL);
root.SetAttribute("schemaVersion", "1.0");
root.SetAttribute("firstprefix:attrOne", "first attribute");
root.SetAttribute("secondprefix:attrTwo", "second attribute with different prefix");
doc.AppendChild(root);
Unfortunately, what I'm getting for the second attribute with the second prefix is no prefix at all. It's just "attrTwo"--like the schemaVersion attribute.
So, is there a way to have different prefixes for attributes in the root element in C#?

This is just a guide for you. May be you can do:
NameTable nt = new NameTable();
nt.Add("key");
XmlNamespaceManager ns = new XmlNamespaceManager(nt);
ns.AddNamespace("firstprefix", "fp");
ns.AddNamespace("secondprefix", "sp");
root.SetAttribute("attrOne", ns.LookupPrefix("fp"), "first attribute");
root.SetAttribute("attrTwo", ns.LookupPrefix("sp"), "second attribute with different prefix");
This will result in:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rootprefix:rootname schemaVersion="1.0" d1p1:attrOne="first attribute" d1p2:attrTwo="second attribute with different prefix" xmlns:d1p2="secondprefix" xmlns:d1p1="firstprefix" xmlns:rootprefix="ns" />
Hope this will be of any help!

I saw a post on another question that ended up solving the issue. I basically just created a string that had all the xml in it, then used the LoadXml method on an instance of XmlDocument.
string rootNodeXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<rootprefix:rootname schemaVersion=\"1.0\" d1p1:attrOne=\"first attribute\""
+ "d1p2:attrTwo=\"second attribute with different prefix\" xmlns:d1p2=\"secondprefix\""
+ "xmlns:d1p1=\"firstprefix\" xmlns:rootprefix=\"ns\" />";
doc.LoadXml(rootNodeXmlString);

Related

Accessing the xml tag with C# and updating the content

I have an xml file that I converted from pdf to xml.
Example XML looks as follows
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.graphics">
<office:body>
<office:drawing>
<draw:page draw:name="page1" draw:style-name="dp2" draw:master-page-name="master-page3">
<draw:frame draw:style-name="gr9" draw:text-style-name="P10" draw:layer="layout" svg:width="1.242cm" svg:height="0.357cm" svg:x="17.055cm" svg:y="11.787cm">
<draw:text-box>
<text:p text:style-name="P2"><text:span text:style-name="T6">Example</text:span></text:p>
</draw:text-box>
</draw:frame>
</draw:page>
</office:drawing>
</office:body>
</office:document>
The C# code I'm trying to use:
XmlDocument doc = new XmlDocument();
XmlNamespaceManager namespaces = new XmlNamespaceManager(doc.NameTable);
namespaces.AddNamespace("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
doc.Load("invoiceto.xml");
doc.SelectSingleNode("/draw:frame/draw:text-box/text:p/text:span", namespaces).InnerText = "new value";
I get this error
'' text 'namespace prefix not defined.'
I want to replace the text of example with C# but how can I get to the <text: span text: style-name = "T6"> tag with C#?
First of all the prefix added to XmlNamespaceManager shouldn't include the xmlns part. Then you also need to add the prefix text besides draw because both will be used in the XPath expression for calling SelectSingleNode. Last, since the element <draw:frame> isn't the root element you need to either specify full path starting from the root or start the XPath using // (the descendant-or-self axis) instead:
XmlNamespaceManager namespaces = new XmlNamespaceManager(doc.NameTable);
namespaces.AddNamespace("draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
namespaces.AddNamespace("text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0");
doc.SelectSingleNode("//draw:frame/draw:text-box/text:p/text:span", namespaces).InnerText = "new value";
dotnetfiddle demo

Update XML Document in WPF C#

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);

Unable to use XmlDocument.SelectSingleNode on XML with Two Namespaces

I'm trying to parse the following XML:
<?xml version="1.0" encoding="utf-8"?>
<A2AAnf:MPPPPPP xsi:schemaLocation="urn:A2AAnf:xsd:$MPPPPPP.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:A2AAnf="urn:A2AAnf:xsd:$MPPPPPP">
<A2AAnf:Num>0</A2AAnf:Num>
<A2AAnf:FIT xmlns="urn:iso:std:iso:20022:xsd:003.001">
<Hdr>
<Inf>
<Mtd>TEST</Mtd>
</Inf>
</Hdr>
</A2AAnf:FIT>
I want to access the <Mtd> tag.
XMLQuire shows the path to be /A2AAnf:MPPPPPP/A2AAnf:FIT/dft:Hdr/dft:Inf/dft:Mtd, but when I'm trying to parse it using the following code:
XmlDocument xmldocument = new XmlDocument();
var xmlNameSpaceManager = new XmlNamespaceManager(xmldocument.NameTable);
xmlNameSpaceManager.AddNamespace("A2AAnf", "urn:A2AAnf:xsd:$MPPPPPP");
try
{
xmldocument.LoadXml(m_XML);
var node = xmldocument.SelectSingleNode("/A2AAnf:MPPPPPP/A2AAnf:FIT/dft:Hdr/dft:Inf/dft:Mtd", xmlNameSpaceManager);
}
I receive the following error:
namespace prefix 'dft' is not defined
And since I can't find "dft" in my XML, I also tried to remove the "dft" prefix and search for the same string without "dft". This time, nothing was returned.
What am I missing?
You have to add dft to your XmlNamespaceManager:
var xmlNameSpaceManager = new XmlNamespaceManager(xmldocument.NameTable);
xmlNameSpaceManager.AddNamespace("A2AAnf", "urn:A2AAnf:xsd:$MPPPPPP");
xmlNameSpaceManager.AddNamespace("dft", "urn:iso:std:iso:20022:xsd:003.001");
The prefixes you use in your XPath query have nothing to do with the prefixes used in the XML document. They are instead the prefixes you define in your XmlNamespaceManager.

Write reference to xsd/schema in output from XElement.Save()

In C#, assume that I have an XElement (say myXElement) containing some XML structure. By calling
myXElement.Save("/path/to/myOutput.xml");
the XML is written to a text file. However, I would like this textfile to include a reference to a (local) xsd-file (an XML schema). That is, I would like the output to look something like this...
<?xml version="1.0" encoding="utf-8" ?>
<MyElement
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="MySchema.xsd">
...
How can I do this?
On the root element, just add an attribute:
Example 1:
XmlDocument d = new XmlDocument();
XmlElement e = d.CreateElement("MyElement");
XmlAttribute a = d.CreateAttribute("xsi", "noNamespaceSchemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
a.Value = "MySchema.xsd";
d.AppendChild(e);
e.Attributes.Append(a);
Example 2:
XDocument d = new XDocument();
XElement e = new XElement("MyElement");
XAttribute a = new XAttribute(XName.Get("noNamespaceSchemaLocation", "http://www.w3.org/2001/XMLSchema-instance"), "MySchema.xsd");
d.Add(e);
e.Add(a);
You should use XDocument instead of XElement as this contains methods for getting and setting the XML declaratuion etc
http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.aspx

How to correctly parse an XML document with arbitrary namespaces

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.

Categories

Resources