Why xmlns appears at the parents' node in DGML file writing? - c#

When I save a DGML file, unnecessary XNamespace appears.
This is a code of saving DGML file.
public void saveDGMLFile()
{
Debug.Log("Save DGML file!");
XNamespace xNamespace = "http://schemas.microsoft.com/vs/2009/dgml";
xDoc = new XDocument();
XElement root = new XElement(
xNamespace + "DirectedGraph",
new XAttribute("name", "root"));
xDoc.Add(root);
XElement parent = xDoc.Root;
XElement nodes = new XElement("Nodes");
foreach (var e in exploded_positions)
{
XElement item = new XElement("Node");
item.SetAttributeValue("Id", e.Item1.name);
item.SetAttributeValue("Category", 0);
item.SetAttributeValue(XmlConvert.EncodeName("start_position"), (e.Item2.x + " " + e.Item2.y + " " + e.Item2.z));
item.SetAttributeValue(XmlConvert.EncodeName("end_position"), (e.Item3.x + " " + e.Item3.y + " " + e.Item3.z));
nodes.Add(item);
}
parent.Add(nodes);
XElement links = new XElement("Links");
XElement link = new XElement("Link");
links.Add(link);
parent.Add(links);
XElement categories = new XElement("Categories");
XElement category = new XElement("category");
categories.Add(category);
parent.Add(categories);
xDoc.Save(outputFileName);
}
And this is an output DGML file.
<?xml version="1.0" encoding="utf-8"?>
<DirectedGraph name="root" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes xmlns="">
<Node Id="PTC_EXP_Blasensensor-Adapter-+-" Category="0" start_position="0 0 0" end_position="0 0 -0.7573751" />
<Node Id="PTC_EXP_BML_Mutter_UNF-2B_1-14-" Category="0" start_position="0 0 0" end_position="0 0.7573751 0" />
<Node Id="PTC_EXP_BUSAKSHAMBAN_OR1501300N" Category="0" start_position="0 0 0" end_position="0.7573751 0 0" />
</Nodes>
<Links xmlns="">
<Link />
</Links>
<Categories xmlns="">
<category />
</Categories>
</DirectedGraph>
As you can see, xmlns="" of XNameSpace appears after the parents' node, Nodes, Links, and Categories.
How can I remove it?

It's because when you set the namespace on the root element to http://schemas.microsoft.com/vs/2009/dgml, it's only affecting that element, it doesn't become the default for the whole document - child elements you add to it will still have a default/empty namespace.
That's why, when the XML is output those elements have the xmlns attribute to distinguish that they are not in the same namespace.
To change that, when creating the child elements you can add the namespace as you have done for the DirectedGraph root element - for example:
XElement nodes = new XElement(xNamespace + "Nodes");
Once they have the same element as the root node, they will no longer be output with an empty xmlns attribute.
Alternatively, see here for a method to do that for all child nodes in a document.

Related

System.InvalidOperationException when modifiying value in an xml file in C#

So i... Have this snippet of code what writes to an existing xml file... the code to me is VERY simple...
XElement element;
XDocument xdoc = XDocument.Load(FileLoc);
element = xdoc.Elements(XName.Get("gold", "http://schemas.datacontract.org/2004/07/DumaLegend")).Single();
element.Value = Gold.Text;
Good Right? good! but why does it give out that error which means that it can't find the thing? it's a very valid thing....
here is the xml file:
<?xml version="1.0" encoding="utf-8"?>
<Save xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/DumaLegend">
<saveInfo>
<energyPieces>0</energyPieces>
<fullEnergyCells>4</fullEnergyCells>
<fullHearts>4</fullHearts>
<globalSwitches xmlns:d3p1="a">
<d3p1:switchList xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
</globalSwitches>
<gold>0</gold>
<hasBigFireball>false</hasBigFireball>
<hasCombo>false</hasCombo>
<hasCrossbow>false</hasCrossbow>
<hasDash>false</hasDash>
<hasDashUpgrade>false</hasDashUpgrade>
<hasDoubleJump>false</hasDoubleJump>
<hasFireball>false</hasFireball>
<hasHookshot>false</hasHookshot>
<hasInvisPot>false</hasInvisPot>
<hasSecondCombo>false</hasSecondCombo>
<hasShieldUpgrade>false</hasShieldUpgrade>
<hasSmallFireball>false</hasSmallFireball>
<heartPieces>0</heartPieces>
<heroPosOnMap>0</heroPosOnMap>
<heroTokens>0</heroTokens>
<itemSlot1 xmlns:d3p1="http://schemas.datacontract.org/2004/07/DumaLegend.Objects.Consumables" i:nil="true" />
<itemSlot2 xmlns:d3p1="http://schemas.datacontract.org/2004/07/DumaLegend.Objects.Consumables" i:nil="true" />
<lives>3</lives>
<worldsUnlocked>0</worldsUnlocked>
<worldsUnlockedOnMap>0</worldsUnlockedOnMap>
</saveInfo>
<saveSlot>0</saveSlot>
</Save>
Use xdoc.Descendants(XName.Get("gold", "http://schemas.datacontract.org/2004/07/DumaLegend")).
From the docs for Elements
Returns a filtered collection of the child elements of this element or document, in document order. Only elements that have a matching XName are included in the collection.
There is only one child elements of your document, and that is the Save element.
What you are looking for is at the path Save/saveInfo/gold. So you can either use Elements like this:
XNamespace ns = "http://schemas.datacontract.org/2004/07/DumaLegend";
var gold = doc.Elements(ns + "Save")
.Elements(ns + "saveInfo")
.Elements(ns + "gold")
.Single();
Or you can use Descendants, which will search all child elements recursively.
XNamespace ns = "http://schemas.datacontract.org/2004/07/DumaLegend";
var gold = doc.Descendants(ns + "gold").Single();

Get rid of xmlns attribute when adding node to existing xml

I have an XML-file:
<ns2:root xmlns:ns2="namespace">
<ns2:a>
<ns2:b>some content</b>
<ns2:c>some content</c>
<ns2:d>some content</d>
</a>
</root>
I need to add a new node in the specific place, my code is:
var doc = XDocument.Load(file);
XNamespace ns2 = "namespace";
doc.Element(ns2 + "root").Element(ns2 + "a").Element(ns2 + "c").AddAfterSelf(
new XElement(ns2+"new",
new XElement("new1",
new XElement("new2","some content"),
new XElement("new3", "some content"))));
The output is:
<ns2:root xmlns:ns2="namespace">
<ns2:a>
<ns2:b>some content</b>
<ns2:c>some content</c>
<ns2:new>
<new1 xmlns="">
<new2>some content</new2>
<new3>some content</new3>
</new1>
</new>
<ns2:d>some content</d>
</a>
</root>
Desired output is:
<ns2:root xmlns:ns2="namespace">
<ns2:a>
<ns2:b>some content</b>
<ns2:c>some content</c>
<ns2:new>
<new1>
<new2>some content</new2>
<new3>some content</new3>
</new1>
</new>
<ns2:d>some content</d>
</a>
</root>
How can I avoid adding xmlns atrribute to the node new1?
Edited mistake in desired output.
Add "ns2 +" before every element name that needs to be in the sitemap namespace
var doc = XDocument.Load(file);
XNamespace ns2 = "namespace";
doc.Element(ns2 + "root").Element(ns2 + "a").Element(ns2 + "c").AddAfterSelf(
new XElement(ns2+"new",
new XElement(ns2+"new1",
new XElement(ns2+"new2","some content"),
new XElement(ns2+"new3", "some content"))));
Adding a default namespace before element resolved my issue
XNamespace defaultNs = "http://www.iata.org/IATA/2007/00";
target.AddAfterSelf(new XElement(defaultNs + "Suffix", suffix));

XML Name Space Not Formating Correctly

Building an XML XDocument to push to web service and the root element needs namespace values this is what the XML form should look like....
<shipment-feed xmlns="http://seller.marketplace.sears.com/oms/v5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://seller.marketplace.sears.com/oms/v5 asn.xsd ">
<shipment>
<header>
<asn-number>00601780002</asn-number>
<po-number>0060180</po-number>
<po-date>2009-09-26</po-date>
</header>
<detail>
<tracking-number>UPS1XXX</tracking-number>
<ship-date>2001-01-01</ship-date>
<shipping-carrier>UPS</shipping-carrier>
<shipping-method>GROUND</shipping-method>
<package-detail>
<line-number>1</line-number>
<item-id>AB12345678912345456789123456789CD</item-id>
<quantity>1</quantity>
</package-detail>
</detail>
</shipment>
</shipment-feed>
This is the xml that I'm getting....
<?xml version="1.0" encoding="utf-8"?>
<shipment-feed xmlns="http://seller.marketplace.sears.com/oms/v5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsischemalocation="http://seller.marketplace.sears.com/oms/v5 asn.xsd">
<shipment xmlns="">
<header>
<asn-number>2824565201311</asn-number>
<po-number>2824565</po-number>
<po-date>2013-11-14</po-date>
</header>
<details>
<tracking-number>579040914892</tracking-number>
<ship-date>2013-11-14</ship-date>
<shipping-carrier>FEDEX</shipping-carrier>
<shipping-method>Ground</shipping-method>
<package-details>
<line-number>1</line-number>
<item-id>LTH7XB1MW-EA</item-id>
<quantity>3</quantity>
</package-details>
</details>
</shipment>
<shipment xmlns="">
<header>
<asn-number>2821596201311</asn-number>
<po-number>2821596</po-number>
<po-date>2013-11-13</po-date>
</header>
<details>
<tracking-number>9405515901119923380663</tracking-number>
<ship-date>2013-11-14</ship-date>
<shipping-carrier>USPS</shipping-carrier>
<shipping-method>Priority Mail</shipping-method>
<package-details>
<line-number>1</line-number>
<item-id>CWD93151-EA</item-id>
<quantity>6</quantity>
</package-details>
<package-details>
<line-number>2</line-number>
<item-id>CWD93901-EA</item-id>
<quantity>4</quantity>
</package-details>
</details>
</shipment>
</shipment-feed>
This is the C# code I created....
XNamespace ns1 = "http://seller.marketplace.sears.com/oms/v5";
XNamespace ns2 = "http://www.w3.org/2001/XMLSchema-instance";
XNamespace ns3 = "http://seller.marketplace.sears.com/oms/v5 asn.xsd";
XDocument doc = new XDocument();
XElement root = new XElement(ns1 + "shipment-feed",
new XAttribute("xmlns" , "http://seller.marketplace.sears.com/oms/v5"),
new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
new XAttribute("xsi" + "schemalocation", "http://seller.marketplace.sears.com/oms/v5 asn.xsd"));
doc.Add(root);
int x = 1;
foreach (SearsOrder s in SearsList)
{
XElement shipment = new XElement("shipment",
new XElement("header",
new XElement("asn-number", s.asnnumber),
new XElement("po-number", s.ponumber),
new XElement("po-date", s.podate)),
new XElement("details",
new XElement("tracking-number", s.trackingnum),
new XElement("ship-date", s.shipdate),
new XElement("shipping-carrier", s.carrier),
new XElement("shipping-method", s.method),
s.orderitems.Select(i => new XElement("package-details",
new XElement("line-number", x++),
new XElement("item-id", i.itemid),
new XElement("quantity", i.quantity)))
));
doc.Root.Add(shipment);
x = 1;
}
The fist problem is the first child node I'm not seeing where that is coming from because that node is not even declared until the foreach loop. I was under the impression that I was only adding attributes to the root element.
and the other problem is removing the xml declaration
This sort of the thing is the problem:
new XElement("shipment", ...)
You want the shipment elements to be in the "http://seller.marketplace.sears.com/oms/v5" namespace - so you need to make that explicit. That won't show up in the resulting XML directly, because they'll inherit that as the default namespace specified in the root. Basically, wherever you're creating an element, you probably want to use ns1. So:
new XElement(ns1 + "shipment", new XElement(ns1 + "header", ...), ...)
To understand more about why this is required, you should read up on namespace defaulting in the XML Namespaces specification.
and the other problem is removing the xml declaration
So add an XDeclaration to the XDocument:
XDocument doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"))

How to find the name of the root node of a given xml file

Im using c#.net windows form application. I have a xml file whose name is hello.xml and it goes like this
<?xml version="1.0" encoding="utf-8" ?>
<languages>
<language>
<key>abc</key>
<value>hello how ru</value>
</language>
<language>
<key>def</key>
<value>i m fine</value>
</language>
<language>
<key>ghi</key>
<value>how abt u</value>
</language>
</languages>
How can i get the root node i.e <languages> into a text box. At this time I will have the xml file name. i.e "hello.xml". Using this I should get the root node.
Using LINQ to XML you can do this:
XDocument doc = XDocument.Load("input.xml");
string rootLocalName = doc.Root.Name.LocalName;
textBox1.Text = '<' + rootLocalName + '>';
With XmlDocument you can use this:
XmlDocument doc = new XmlDocument();
doc.Load("input.xml");
string rootName = doc.SelectSingleNode("/*").Name;
Or use the XmlDocument DocumentElement property as shown here:
XmlDocument doc = new XmlDocument();
doc.Load("hello.xml");
string root = doc.DocumentElement.Name;
textBox1.Text = "<" + root + ">";

XElement default namespace on attributes provides unexpected behaviour

I am having trouble creating an XML document that contains a default namespace and a named namespace, hard to explain easier to just show what I am trying to produce...
<Root xmlns="http://www.adventure-works.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:SchemaLocation="http://www.SomeLocatation.Com/MySchemaDoc.xsd">
<Book title="Enders Game" author="Orson Scott Card" />
<Book title="I Robot" author="Isaac Asimov" />
</Root>
but what I end up with is this...
<Root xmlns="http://www.adventure-works.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:SchemaLocation="http://www.SomeLocatation.Com/MySchemaDoc.xsd">
<Book p3:title="Enders Game" p3:author="Orson Scott Card" xmlns:p3="http://www.adventure-works.com" />
<Book p3:title="I Robot" p3:author="Isaac Asimov" xmlns:p3="http://www.adventure-works.com" />
</Root>
The code that I wrote to produce this XML snippet is this...
XNamespace aw = "http://www.adventure-works.com";
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
XElement root = new XElement(aw + "Root",
new XAttribute("xmlns", "http://www.adventure-works.com"),
new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
new XAttribute(xsi + "SchemaLocation", "http://www.SomeLocatation.Com/MySchemaDoc.xsd"),
new XElement(aw + "Book",
new XAttribute(aw + "title", "Enders Game"),
new XAttribute(aw + "author", "Orson Scott Card")),
new XElement(aw + "Book",
new XAttribute(aw + "title", "I Robot"),
new XAttribute(aw + "author", "Isaac Asimov")));
based on an example on MSDN
****EDIT****
Ok, with some more experimentation I am now very confused on how XML namespaces work....
if I remove the aw + theattribute I get what I was after...but now it seems that what I was after is not actually what I expected. I thought that namespaces were inherited from their parents, is this not true of attributes as well? because, this code to read the attributes does not work as I expected...
XElement xe = XElement.Parse(textBox1.Text);
XNamespace aw = "http://www.adventure-works.com";
var qry = from x in xe.Descendants(aw + "Book")
select (string)x.Attribute(aw + "author");
However if I remove the aw + on the attribute its ok, leading me to assume that I cannot have attributes in the default namespace. Is this correct?
Good question. I dug around a bit, and found this bit of the XML spec:
A default namespace declaration
applies to all unprefixed element
names within its scope. Default
namespace declarations do not apply
directly to attribute names; the
interpretation of unprefixed
attributes is determined by the
element on which they appear.
It later goes on to give this example:
For example, each of the bad empty-element tags is illegal in the following:
<!-- http://www.w3.org is bound to n1 and n2 -->
<x xmlns:n1="http://www.w3.org"
xmlns:n2="http://www.w3.org" >
<bad a="1" a="2" />
<bad n1:a="1" n2:a="2" />
</x>
However, each of the following is legal, the second because the default namespace does not > apply to attribute names:
<!-- http://www.w3.org is bound to n1 and is the default -->
<x xmlns:n1="http://www.w3.org"
xmlns="http://www.w3.org" >
<good a="1" b="2" />
<good a="1" n1:a="2" />
</x>
So basically, it looks like attribute names don't get namespaces by default, which explains everything you've seen :)
XElement doc = XElement.Parse(ToXml());
doc.DescendantsAndSelf().Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
var ele = doc.DescendantsAndSelf();
foreach (var el in ele)
el.Name = ns != null ? ns + el.Name.LocalName : el.Name.LocalName;
For anyone else who spent 2 days trying to find an answer.

Categories