XDocument Add multiple XElements - c#

In my Windows Phone 8 C#/XAML .NET 4.5 Project, I'm trying to create an XDocument with similar structure:
<element1>
<subelement1>
</subelement1>
<subelement2>
...etc...
</subelement2>
</element1>
<element2>
<subelement1>
</subelement1>
<subelement2>
...etc...
</subelement2>
</element2>
The method creating the document looks like (simplified for the question purposes):
... createXML()
{
XDocument doc = new XDocument();
XElement elem1 = new XElement("element1");
elem1.Add(new XElement("subelement1"));
XElement elem2 = new XElement("element2");
doc.Add(elem1);
doc.Add(elem2);
}
But I keep getting InvalidOperationException saying that it would create a invalid document structure.
I know why - it would cause the document to have multiple "root nodes" - but I effectively need it that way.
This structure is needed for webservice done by third party, which recieves the document as a string.
So the question is "How to achieve this structure? Should I use some other XObject instead?"
(I know that probably the most simple solution would be to use collection of XElements...just askin' if there is another way out of curiosity)

The structure that you specified at the top of the post is illegal, because valid XML documents must have a single root element; your document has two elements at the top level, which is not allowed.
You can solve this problem by adding a root element at creation time, and then discarding it when reading the document;
document = new XDocument(new XElement("root", elem1, elem2));

Related

Modify xml with Xmldocument in C#

I am trying to edit the values of an xml document following the instructions found on another post here How to modify existing XML file with XmlDocument and XmlNode in C# .
here is my code
XmlDocument xml = new XmlDocument();
xml = xml.Load(#"https://www.aade.gr/sites/default/files/2020-09/SampleXML_1.1%20%20%28%CE%A4%CE%99%CE%9C-%CE%A0%CE%A9%CE%9B%CE%97%CE%A3%CE%97%CE%A3_%CE%91%CE%A5%CE%A4%CE%9F%CE%A4%CE%99%CE%9C%29%20.xml");
XmlNodeList aNodes = xml.SelectNodes("/InvoicesDoc/invoice/issuer/vatNumber");
foreach (XmlNode aNode in aNodes)
{
XmlAttribute vatAttribute = aNode.Attributes["vatNumber"];
vatAttribute.Value = "123456789";
}
xml.Save(#"C:\Users\Kostas\Desktop\mydata\infinal.xml");
My problem is that XmlNodeList aNodes will return empty; i have tried to change the xml.SelectNodes("/InvoicesDoc/invoice/issuer/vatNumber") to xml.SelectNodes("/InvoicesDoc/invoice/issuer") and all the way up to single xml.SelectNodes("/InvoicesDoc") but still XmlNodeList aNodes will return empty.
First attempts i loaded the XML doc from file and had the issue. Then i thought maybe something wrong with the file so changed the load of the file directly from the site provides this xml template i need to work on. Both options will load the file fine as i can see it when is saved but my changes will not complete since aNodes is empty and foreach loop will skip straight away.
What am i doing wrong?
thanks for your help in advance.
ps this is the xml i need to edit
https://www.aade.gr/sites/default/files/2020-09/SampleXML_1.1%20%20%28%CE%A4%CE%99%CE%9C-%CE%A0%CE%A9%CE%9B%CE%97%CE%A3%CE%97%CE%A3_%CE%91%CE%A5%CE%A4%CE%9F%CE%A4%CE%99%CE%9C%29%20.xml
Update: I just tried with another xlm found in microsoft example called books on https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms762271(v=vs.85)
XmlNodelist will also return null/empty when i look for /catalog/book . So the good side is that there is no problem with original xml file i need to edit and the bad side is that still i cannot figure out what i am doing wrong.
XmlNodeList aNodes returns null because the xml contains these namespace declarations:
<InvoicesDoc xmlns=\"http://www.aade.gr/myDATA/invoice/v1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xsi:schemaLocation=\"http://www.aade.gr/myDATA/invoice/v1.0/InvoicesDoc-v0.6.xsd\"
xmlns:icls=\"https://www.aade.gr/myDATA/incomeClassificaton/v1.0\"
xmlns:ecls=\"https://www.aade.gr/myDATA/expensesClassificaton/v1.0\">
You need to manage your xml doing something like this:
XmlDocument xml = new XmlDocument();
xml.Load(#"https://www.aade.gr/sites/default/files/2020-09/SampleXML_1.1%20%20%28%CE%A4%CE%99%CE%9C-%CE%A0%CE%A9%CE%9B%CE%97%CE%A3%CE%97%CE%A3_%CE%91%CE%A5%CE%A4%CE%9F%CE%A4%CE%99%CE%9C%29%20.xml");
XmlNamespaceManager manager = new XmlNamespaceManager(xml.NameTable);
manager.AddNamespace("InvoicesDoc", "http://www.aade.gr/myDATA/invoice/v1.0");
//Example to get the root element
XmlNodeList root = xml.SelectNodes("/InvoicesDoc:InvoicesDoc", manager);
//Example to get the VatNumber tag
XmlNodeList aNodes =xml.SelectNodes("/InvoicesDoc:InvoicesDoc/InvoicesDoc:invoice/InvoicesDoc:issuer/InvoicesDoc:vatNumber", manager);

How to change the data within elements in a XML file using C#?

I'm kind of new to XML files in C# ASP.NET. I have a XML in the below format:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Installation>
<ServerIP>192.168.20.110</ServerIP>
<DB_Name>USTCKT1</DB_Name>
<Username>jorame</Username>
<Password>Cru$%e20</Password>
<Table_PreFix>TCK</Table_PreFix>
</Installation>
I need to change the values within each element. For example, when an user clicks I should be able to replace 192.168.20.110 with 192.168.1.12.
How can I accomplish this? Any help will be really appreciated.
You should look at using the methods in the XDocument class. http://msdn.microsoft.com/en-us/library/bb301598.aspx
Specifically look at the methods: Load(string) - to load an XML file, Element() - to access a specific element and Save(string) - to save the XML document. The page on Element() has some sample code which can help.
http://msdn.microsoft.com/en-us/library/system.xml.linq.xcontainer.element.aspx
You can do something like this using the XDocument class:
XDocument doc = XDocument.Load(file.xml);
doc.Element("Installation").Element("ServerIP").Value = "192.168.1.12";
//Update the rest of the elements
doc.Save(file.xml);
More Details
If you run into namespace issues when selecting your elements you will need to include the xml namespace in the XElement selectors eg doc.Element(namspace + "Installation")
In general, you can do it in the following steps:
Create a new XmlDocument object and load the content. The content might be a file or string.
Find the element that you want to modify. If the structure of your xml file is too complex, you can use xpath you find what you want.
Apply your modification to that element.
Update your xml file.
Here is a simple demo:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("file.xml"); // use LoadXml(string xml) to load xml string
string path = "/Installation/ServerIP";
XmlNode node = xmlDoc.SelectSingleNode(path); // use xpath to find a node
node.InnerText = "192.168.1.12"; // update node, replace the inner text
xmlDoc.Save("file.xml"); // save updated content
Hope it's helpful.

How to read, store and delete xml data in C#

<SMSWall run="yes" nonstop="False">
<starttime>10:15:25 PM</starttime>
<endtime>10:15:25 PM</endtime>
</SMSWall>
<MediaPlayer run="yes" nonstop="False">
<starttime>10:15:25 PM</starttime>
<endtime>10:15:25 PM</endtime>
</MediaPlayer>
<Bidding run="yes" nonstop="False">
<starttime>10:15:25 PM</starttime>
<endtime>10:15:25 PM</endtime>
</Bidding>
This is my xml file. Now I want to read the xml, store the value in a variable (and also want the data to read from the string), and leave the .xml file so that it can be deleted.I also want to read the value node wise like:
XmlDocument document = new XmlDocument();
document.Load("Schedule.xml");
XmlNode ht=document.SelectSingleNode("SMSWall/#run");
Your current XML file is not in correct format (i.e save it as .xml and open it with IE, you will give an error). So you should add <root> ... </root> (or other name).
In all I think best way to treat with XML is using System.Xml.Linq library, like XDocument and XElement :
Loading data: XDocument.Load(filename);
Creating item: XElement root = new XElement("Root");
Searching: document.Descendants("Root"), ...
See MSDN for other samples.
I prefer to create a DataSet and then use it like this:
DataSet1 ds = new DataSet1();
ds.ReadXml("test.xml", XmlReadMode.ReadSchema);
//do something
ds.WriteXml("test.xml", XmlReadMode.ReadSchema);
A typed DataSet will provide you typed tables and rows. You can get rows by index or query. You can find more about the here.

Parse an xml document when namespace is no-longer available

I have a number rather large, complex xml documents that I need to loop through. An xmlns is defined at the top of the document however the url this points to is no longer available.
What's the best way to parse the file to get the important data from it using C#?
I tried to load it into a Dataset but would occasionally receive the errors:
The table (endpoint) cannot be the child table to itself in nested relations.
or
Cannot add a SimpleContent column to a table containing element columns or nested relations.
XPath was my next port of call but I had problems because of the lack of namespace.
I suspect this is seriously limiting my options, but does anyone have any suggestions?
Snippet of the XML document:
<?xml version="1.0" encoding="UTF-8"?>
<cdr:cdr_set xmlns:cdr="http://www.naturalconvergence.com/schema/cdr/v3/cdr">
<!-- Copyright (c) 2001-2009, all rights reserved -->
<cdr:cdr xmlns:cdr="http://www.naturalconvergence.com/schema/cdr/v3/cdr">
<cdr:call_id>2040-1247062136726-5485131</cdr:call_id>
<cdr:cdr_id>1</cdr:cdr_id>
<cdr:status>Normal</cdr:status>
<cdr:responsibility>
<cdr:tenant id="17">
<cdr:name>SpiriTel plc</cdr:name>
</cdr:tenant>
<cdr:site id="45">
<cdr:name>KWS</cdr:name>
<cdr:time_zone>GB</cdr:time_zone>
</cdr:site>
</cdr:responsibility>
<cdr:originator type="sipGateway">
<cdr:sipGateway id="3">
<cdr:name>Audiocodes-91</cdr:name>
</cdr:sipGateway>
</cdr:originator>
<cdr:terminator type="group">
<cdr:group>
<cdr:tenant id="17">
<cdr:name>SpiriTel plc</cdr:name>
</cdr:tenant>
<cdr:type>Broadcast</cdr:type>
<cdr:extension>6024</cdr:extension>
<cdr:name>OLD PMS DDIS DO NOT USE</cdr:name>
</cdr:group>
</cdr:terminator>
<cdr:initiation>Dialed</cdr:initiation>
<cdr:calling_number>02087893850</cdr:calling_number>
<cdr:dialed_number>01942760142</cdr:dialed_number>
<cdr:target>6024</cdr:target>
<cdr:direction>Inbound</cdr:direction>
<cdr:disposition>No Answer</cdr:disposition>
<cdr:timezone>GB</cdr:timezone>
<cdr:origination_timestamp>2009-07-08T15:08:56.727+01:00</cdr:origination_timestamp>
<cdr:release_timestamp>2009-07-08T15:09:26.493+01:00</cdr:release_timestamp>
<cdr:release_cause>Normal Clearing</cdr:release_cause>
<cdr:call_duration>PT29S</cdr:call_duration>
<cdr:redirected>false</cdr:redirected>
<cdr:conference>false</cdr:conference>
<cdr:transferred>false</cdr:transferred>
<cdr:estimated>false</cdr:estimated>
<cdr:interim>false</cdr:interim>
<cdr:segments>
<cdr:segment>
<cdr:originationTimestamp>2009-07-08T15:08:56.727+01:00</cdr:originationTimestamp>
<cdr:initiation>Dialed</cdr:initiation>
<cdr:call_id>2040-1247062136726-5485131</cdr:call_id>
<cdr:originator type="sipGateway">
<cdr:sipGateway id="3">
<cdr:name>Audiocodes-91</cdr:name>
</cdr:sipGateway>
</cdr:originator>
<cdr:termination_attempt>
<cdr:termination_timestamp>2009-07-08T15:08:56.728+01:00</cdr:termination_timestamp>
<cdr:terminator type="group">
<cdr:group>
<cdr:tenant id="17">
<cdr:name>SpiriTel plc</cdr:name>
</cdr:tenant>
<cdr:type>Broadcast</cdr:type>
<cdr:extension>6024</cdr:extension>
<cdr:name>OLD PMS DDIS DO NOT USE</cdr:name>
</cdr:group>
</cdr:terminator>
<cdr:provided_address>01942760142</cdr:provided_address>
<cdr:direction>Inbound</cdr:direction>
<cdr:disposition>No Answer</cdr:disposition>
</cdr:termination_attempt>
</cdr:segment>
</cdr:segments>
</cdr:cdr>
...
</cdr:cdr_set>
Each entry is essentially the same but there are sometimes differences such as some of the fields may be missing, if they aren't required.
These values in an xml file are identifiers, not locators. Unless you are expecting to download a schema, it is not needed at all, and can be "flibble" if needed. I expect the best thing would be to just load it into XmlDocument / XDocument and try to access the data.
For example:
XmlDocument doc = new XmlDocument();
doc.Load("cdr.xml");
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("cdr", "http://www.naturalconvergence.com/schema/cdr/v3/cdr");
XmlElement el = (XmlElement)doc.SelectSingleNode(
"cdr:cdr_set/cdr:cdr/cdr:originator", ns);
Console.WriteLine(el.GetAttribute("type"));
or to loop over the cdr elements:
foreach (XmlElement cdr in doc.SelectNodes("/cdr:cdr_set/cdr:cdr", ns))
{
Console.WriteLine(cdr.SelectSingleNode("cdr:call_id", ns).InnerText);
}
Note that the aliases used in the document are largely unrelated to the aliases used in the XmlNamespaceManager, hence you need to re-declare it. I could have used x as my alias in the C# just as easily.
Of course, if you prefer to work with an object model; run it through xsd (where cdr.xml is your example file):
xsd cdr.xml
xsd cdr.xsd /classes
Now you can load it with XmlSerializer.
alternativley load it into an Xdocument and use linq2XML? ... although you might just get the same error.
I don't know what data you want, so its hard to suggest a query.
I personally prefer the use of XDocument to xmlDocument now in most cases.
the only problem with the automatic generation of an XSD is that it can get your datatypes pretty badly wrong if you are not using a good sized chunk of sample data.

Read in an XML String with Namespaces for Use in an XSL Transformation

In an ASP.NET 2.0 website, I have a string representing some well-formed XML. I am currently creating an XmlDocument object with it and running an XSL transformation for display in a Web form. Everything was operating fine until the XML input started to contain namespaces.
How can I read in this string and allow namespaces?
I've included the current code below. The string source comes from an HTML encoded node in a WordPress RSS feed.
XPathNavigator myNav= myPost.CreateNavigator();
XmlNamespaceManager myManager = new XmlNamespaceManager(myNav.NameTable);
myManager.AddNamespace("content", "http://purl.org/rss/1.0/modules/content/");
string myPost = HttpUtility.HtmlDecode("<post>" +
myNav.SelectSingleNode("//item[1]/content:encoded", myManager).InnerXml +
"</post>");
XmlDocument myDocument = new XmlDocument();
myDocument.LoadXml(myPost.ToString());
The error is on the last line:
"System.Xml.XmlException: 'w' is an undeclared namespace. Line 12, position 201. at System.Xml.XmlTextReaderImpl.Throw(Exception e) ..."
Your code looks right.
The problem is probably in the xml document you're trying to load.
It must have elements with a "w" prefix, without having that prefix declared in the XML document
For example, you should have:
<test xmlns:w="http://...">
<w:elementInWNamespace />
</test>
(your document is probably missing the xmlns:w="http://")
Gut feel - one of the namespaces declared in //content:encoding is being dropped (probably because you're using the literal .InnerXml property)
What's 'w' namespace evaluate to in the myNav DOM? You'll want to add xmlns:w= to your post node. There will probably be others too.

Categories