Generate XML in C# Using Specific XML Namespace - c#

Target XML I am trying to achieve:
<Row><Data ss:Type="String">value</Data></Row>
Output I am currently getting:
<Row xmlns=""><Data Type="Number">0</Data></Row>
I am trying to create that target XML code using the System.Xml library, but the namespace prefix is not showing up when I create new elements. Snippet of the code that is generating the above output:
XmlElement eRow = xDoc.CreateElement("Row");
XmlElement eData = xDoc.CreateElement("Data");
XmlAttribute xAt = xDoc.CreateAttribute("ss", "Type", null);
xAt.Value = "Number";
eData.Attributes.Append(xAt);
eData.InnerText = "0";
eRow.AppendChild(eData);
I am trying to append this XML to a file that already exists. I have loaded the file as
XmlDocument xTemp = new XmlDocument();
xTemp.Load(templatePath);
and there are already namespaces in DocumentElement.Attributes that have already declared the prefix that I want to use: <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">. Essentially, I am trying to get the "ss" prefix to show up before "Type" like in the target I provided above. Additionally, the output is displaying xmlns="" as an attribute in the "Row" tag, something that I never added. I assume these are both issues with the namespace not being declared, but as mentioned above, it should already be declared in the original document that I loaded.
How can I generate the target XML code I want?

You have created your attribute xAt without specifying the namespace uri, which is equivalent to empty string namespace uri (see the corresponding MSDN doc here), that is certainly why you get the <Row xmlns="">
Actually you need to specify the exact namespace uri for it to work as you expect it.
Let me illustrate using the namespace uri you have given in your question (Illustration very similar to your initial code but might have a few small differences that you can easily modify).
String namespaceUri = "urn:schemas-microsoft-com:office:spreadsheet";
XmlDocument xDoc = new XmlDocument();
XmlElement workbook = xDoc.CreateElement("ss", "Workbook", namespaceUri);
XmlElement rows = xDoc.CreateElement("Rows");
At this step I can assume that I have an XmlDocument similar to what you have after initially loading your file. My XmlDocument has the workbook node as its DocumentElement, it uses the given prefix and namespace uri.
Now we can create the attribute:
var attribute = xDoc.CreateAttribute("ss", "Type", "urn:schemas-microsoft-com:office:spreadsheet");
attribute.Value = "String";
The namespace uri should be specified correctly otherwise it won't be correctly rendered. When this attribute is used, since the namespace it is refering to is found on the nesting element (workbook), it is not necessary to mention it again here, and the framework will automatically remove the reference to the namespace uri.
Now we can go ahead and create the Row and data elements and add the attribute to the collection of attributes of the Data element.
XmlElement eRow = xDoc.CreateElement("Row");
XmlElement eData = xDoc.CreateElement("Data");
eData.Attributes.Append(attribute);
eData.InnerText = "value";
eRow.AppendChild(eData);
rows.AppendChild(eRow);
workbook.AppendChild(rows);
xDoc.AppendChild(workbook);
We can then display the document, for example with:
Console.WriteLine(xDoc.OuterXml);
Result:
<ss:Workbook xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"><Rows><Row><Data ss:Type="String">value</Data></Row></Rows></ss:Workbook>
I hope this helps.

Related

Manipulate SSIS Package Using XmlDocument

I'm downgrading a SSIS package from 2014 to 2012. This processneeds to some search/replace and I know to do that manually.
Now I'm trying to write a C# program to do the job.
This is the XML in .DTSX file.
<?xml version="1.0"?>
<DTS:Executable xmlns:DTS="www.microsoft.com/SqlServer/Dts"
DTS:refId="Package"
DTS:CreationDate="8/10/2016 11:39:29 AM"
DTS:CreationName="Microsoft.Package"
DTS:CreatorComputerName="FD89D67CEC21F"
DTS:CreatorName="CORP\ZKHEKRZ"
DTS:DTSID="{63B7F2FE-2D47-4710-82DF-83C92B7010B5}"
DTS:ExecutableType="Microsoft.Package"
DTS:LastModifiedProductVersion="12.0.4100.1"
DTS:LocaleID="1033"
DTS:ObjectName="CubeDailyBackup"
DTS:PackageType="5"
DTS:VersionBuild="131"
DTS:VersionGUID="{8F0EAFC9-D73C-463C-8D5E-CB33C858D0D5}">
<DTS:Property DTS:Name="PackageFormatVersion">8</DTS:Property>
....... more lines below
At first step I need to replace
<DTS:Property DTS:Name="PackageFormatVersion">8</DTS:Property>
with
<DTS:Property DTS:Name="PackageFormatVersion">6</DTS:Property>
To do the job, I load .DTSX file using XmlDocument and then find the node using below code
private XmlDocument Pkg14 = new XmlDocument();
Pkg14.Load("mypackage.dtsx");
XmlNodeList xnList = Pkg14.SelectNodes("//DTS:Property[#DTS:Name='PackageFormatVersion']");
Executing the third line throws below error:
Namespace Manager or XsltContext needed. This query has a prefix,
variable, or user-defined function
Please advise.
EDIT:
I tried adding below code before Pkg14.SelectNodex but it did not help:
XmlNamespaceManager xmlnsManager = new XmlNamespaceManager(Pkg14.NameTable);
xmlnsManager.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
xmlnsManager.AddNamespace("DTS", "www.microsoft.com/sqlserver/dts/tasks/webservicetask");
..... and adding more namespace
Make sure you have the correct URL mapped to the prefix (DTS prefix in your code mapped to a different URI from the one in your XML). Also make sure you pass the XmlNamespaceManager along with the XPath query to SelectNodes() or SelectSingleNode() method :
Pkg14.Load("mypackage.dtsx");
XmlNamespaceManager xmlnsManager = new XmlNamespaceManager(Pkg14.NameTable);
xmlnsManager.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
xmlnsManager.AddNamespace("DTS", "www.microsoft.com/SqlServer/Dts");
XmlNode node = Pkg14.SelectSingleNode("//DTS:Property[#DTS:Name='PackageFormatVersion']", xmlnsManager);
node.InnerText = "6";
//save the modified XmlDocument back to file
Pkg14.Save("mypackage.dtsx");
dotnetfiddle demo

Why does having a xmlns cause my C# program not to read XML?

I have a C# program that attempts to read the following xml, but can't read any elements:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Comments Here -->
<FileFeed
xmlns="http://www.mycompany.com/schemas/xxx/FileFeed/V1"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.somecompany.com/schemas/xxx/FileFeed/V1
FileFeed.xsd"
RecordCount = "1">
<Object>
<ID>PAMSMOKE110113xxx</ID>
<CorpID>12509</CorpID>
<AnotherID>201654702345</AnotherID>
<TimeStamp>2013-09-03</TimeStamp>
<Type>Some Type</Type>
<SIM_ID>89011704258012600767</SIM_ID>
<Code>ZZZ</Code>
<Year>2013</Year>
</Object>
</FileFeed>
With the above XML my C# program is unable to read any elements.. For instance the ID Element is always NULL.
Now if I simply remove the first xmlns from the above XML, my program can read all the elements without any issues. The problem is I have to process the XML file in the format that's given to me, and can't change the file format. My program reads the below XML just fine: Note the line xmlns="http://www.mycompany.com/schemas/xxx/FileFeed/V1" is removed.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Comments Here -->
<FileFeed
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.somecompany.com/schemas/xxx/FileFeed/V1
FileFeed.xsd"
RecordCount = "1">
<Object>
<ID>PAMSMOKE110113xxx</ID>
<CorpID>12509</CorpID>
<AnotherID>201654702345</AnotherID>
<TimeStamp>2013-09-03</TimeStamp>
<Type>Some Type</Type>
<SomeNumber>89011704258012600767</SomeNumber>
<Code>ZZZ</Code>
<Year>2013</Year>
</Object>
</FileFeed>
I realize I'm not posting any code, but just wondering what possible issue could I be having, where simply removing the xmlns line resolves everything??
Your problem is with xml namespaces
Using Linq2Xml
XNamespace ns = "http://www.mycompany.com/schemas/xxx/FileFeed/V1";
var xDoc = XDocument.Load(fname);
var id = xDoc.Root.Element(ns + "Object").Element(ns + "ID").Value;
Your root element FileFeed has a namespace attribute. This means that each element inside it also uses that namespace.
The Element method takes an XName as its argument. Usually you use a string which gets implicitly converted into an XName.
If you want to include a namespace you create an XNamespace and add the string. Since XNamespace overloads the + operator this will also result in an XName.
XDocument doc = XDocument.Load("Test.xml");
// this will be null
XElement objectElementWithoutNS = doc.Root.Element("Object");
XNamespace ns = doc.Root.GetDefaultNamespace();
XElement objectElementWithNS = doc.Root.Element(ns + "Object");
Xml namespaces are more or less like C# namespaces. Would you be able to access a class when its namespace is set or not set?
public namespace My.Company.Schemas {
public class FileFeed
vs
public class FileFeed {
They are two DISTINCT classes! The same applies to XML - by setting a namespace you make it possible to have documents with similar or even the same internal structure but they represent two disctinct documents that are not exchangeable. This is really convenient.
If you'd like to get help on why your actual reading method doesn't consider the namespace, you have to present the C# code. The general rule though is that any reading API makes is possible to set the namespace for actual reading.

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.

Default namespace when created xml document

Is it possible to create children using XmlDocument.CreateElement() and XmlNode.AppendChild() without specifying the namespace and have it use a "default" namespace?
Currently, if I create a root node with a namespace and don't specify the namespace on the every childnode, the output xml will define a blank namespace.
Below is what is generated if I don't specify the namespace for every element I create. Is there a shortcut where I don't have to specify the namespace every time?
<root xmlns="http://example.com">
<child1 xmlns="">
<child2 />
</child1>
</root>
Code:
XmlDocument doc = new XmlDocument();
var rootNode = doc.CreateElement("root", "http://example.com");
doc.AppendChild(rootNode);
var child1Node = doc.CreateElement("child1");
rootNode.AppendChild(child1Node);
var child2Node = doc.CreateElement("child2");
child1Node.AppendChild(child2Node);
If you have create your XML document, and you specify the same namespace for each element in the hierarchy - something like this:
XmlDocument doc = new XmlDocument();
const string xmlNS = "http://www.example.com";
XmlElement root = doc.CreateElement("root", xmlNS);
doc.AppendChild(root);
XmlElement child1 = doc.CreateElement("child1", xmlNS);
root.AppendChild(child1);
child1.AppendChild(doc.CreateElement("child2", xmlNS));
doc.Save(#"D:\test.xml");
then you'll get this output file:
<root xmlns="http://www.example.com">
<child1>
<child2 />
</child1>
</root>
The namespace on the <root> node is inherited down the hierarchy, unless the child elements define something else explicitly.
If you create a new XmlElement using doc.CreateElement and you don't specify a XML namespace, then of course, that new element, will have a blank namespace and thus this will be serialized into that XML document you had.
I am not aware of any way to specify a default namespace to use whenever you're creating a new element - if you specify one, the element will use that namespace - if you don't specify one, it's the blank namespace.
If you are using .NET 3.5, I suggest using LINQ to XML, (System.Xml.Linq). Use the XDocument, XElement, and XAttribute classes.
But marc_s's answer is correct, the namespace is inherited.

How to prevent blank xmlns attributes in output from .NET's XmlDocument?

When generating XML from XmlDocument in .NET, a blank xmlns attribute appears the first time an element without an associated namespace is inserted; how can this be prevented?
Example:
XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root",
"whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner"));
Console.WriteLine(xml.OuterXml);
Output:
<root xmlns="whatever:name-space-1.0"><loner xmlns="" /></root>
Desired Output:
<root xmlns="whatever:name-space-1.0"><loner /></root>
Is there a solution applicable to the XmlDocument code, not something that occurs after converting the document to a string with OuterXml?
My reasoning for doing this is to see if I can match the standard XML of a particular protocol using XmlDocument-generated XML. The blank xmlns attribute may not break or confuse a parser, but it's also not present in any usage that I've seen of this protocol.
Thanks to Jeremy Lew's answer and a bit more playing around, I figured out how to remove blank xmlns attributes: pass in the root node's namespace when creating any child node you want not to have a prefix on. Using a namespace without a prefix at the root means that you need to use that same namespace on child elements for them to also not have prefixes.
Fixed Code:
XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root", "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner", "whatever:name-space-1.0"));
Console.WriteLine(xml.OuterXml);
Thanks everyone to all your answers which led me in the right direction!
This is a variant of JeniT's answer (Thank you very very much btw!)
XmlElement new_element = doc.CreateElement("Foo", doc.DocumentElement.NamespaceURI);
This eliminates having to copy or repeat the namespace everywhere.
If the <loner> element in your sample XML didn't have the xmlns default namespace declaration on it, then it would be in the whatever:name-space-1.0 namespace rather than being in no namespace. If that's what you want, you need to create the element in that namespace:
xml.CreateElement("loner", "whatever:name-space-1.0")
If you want the <loner> element to be in no namespace, then the XML that's been produced is exactly what you need, and you shouldn't worry about the xmlns attribute that's been added automatically for you.
Since root is in an unprefixed namespace, any child of root that wants to be un-namespaced has to be output like your example. The solution would be to prefix the root element like so:
<w:root xmlns:w="whatever:name-space-1.0">
<loner/>
</w:root>
code:
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement( "w", "root", "whatever:name-space-1.0" );
doc.AppendChild( root );
root.AppendChild( doc.CreateElement( "loner" ) );
Console.WriteLine(doc.OuterXml);
If possible, create a serialization class then do:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer serializer = new XmlSerializer(yourType);
serializer.Serialize(xmlTextWriter, someObject, ns);
It's safer, and you can control the namespaces with attributes if you really need more control.
I've solved the problem by using the Factory Pattern. I created a factory for XElement objects. As parameter for the instantiation of the factory I've specified a XNamespace object. So, everytime a XElement is created by the factory the namespace will be added automatically. Here is the code of the factory:
internal class XElementFactory
{
private readonly XNamespace currentNs;
public XElementFactory(XNamespace ns)
{
this.currentNs = ns;
}
internal XElement CreateXElement(String name, params object[] content)
{
return new XElement(currentNs + name, content);
}
}
Yes you can prevent the XMLNS from the XmlElement .
First Creating time it is coming : like that
<trkpt lat="30.53597" lon="-97.753324" xmlns="">
<ele>249.118774</ele>
<time>2006-05-05T14:34:44Z</time>
</trkpt>
Change the code : And pass xml namespace
like this
C# code:
XmlElement bookElement = xdoc.CreateElement("trkpt", "http://www.topografix.com/GPX/1/1");
bookElement.SetAttribute("lat", "30.53597");
bookElement.SetAttribute("lon", "97.753324");

Categories