Search code examples
xmlunity-game-engineunityscriptxml-namespacesxmldocument

Declaring XML Namespace at Runtime


I’m trying to write a script for a Unity application (written in Unityscript) which is able to add nodes to an XML document at runtime (using the XmlDocument class). I am encountering issues when trying to declare the 'name' attribute of the new node.

I would like to know how I can create a node with a namespace outside of the default XMLNS attribution (in my case replacing the 'xmlns=' to 'name=') to match the existing markup.

After much time on the MSDN docs I assume this is probably to do with the XmlNameSpaceManager or changing the default namespace, however I am struggling to understand how to implement this (still pretty new to XML in Unity / uJS) so any advice would be very welcome.

Many thanks in advance,

Ryan

Current code:

function Start(){
	//Automatically loads XML doc for save etc
	doc = new XmlDocument();
	doc.Load(Application.dataPath + "/objMetaTest01.xml");
	
	root = doc.DocumentElement;
	Debug.Log("XML Root: " + root.Name);
	
	nodeList = root.SelectNodes("MetaPipeObject");
	Debug.Log("***XML Log Begin ***");
	Debug.Log("Number of Objects: " + nodeList.Count); //returns total number of MPObjs
	
	for (var i = 0; i < nodeList.Count; i++)
	{
		var node = nodeList[i];
		//Debug.Log("Node Name: " + node.Attributes["name"].Value);
		
	}
	
	//Namespace Manager to add namespace to file
	//docNameSpace = new XmlNamespaceManager(doc.NameTable);
	//docNameSpace.AddNamespace("name", doc.DocumentElement.NamespaceURI);
	
	//Debug.Log("Doc DefaultNamespace: " + docNameSpace.DefaultNamespace);
	//Debug.Log("nmsg has 'name' prefix: " + docNameSpace.HasNamespace("name"));
	Debug.Log("***XML Log End ***");
}


public function CreateNewNode(){
	//Creates new node for new objects
	//will be similar to the replace function to be written soon

	//select last MP node to add the new one behind	
	doc.DocumentElement.RemoveAttribute("xlmns");
	
	var lastObjNode = root.SelectSingleNode("MetaPipeObject[last()]");
	var newObjNode = doc.CreateElement("MetaPipeObject", "DogTest");
	
	//Create new attribute test
	//var nameAttr = doc.CreateAttribute("name");
	//newObjNode.Attributes.Append(nameAttr);
	//"name", "nameTest", doc.DocumentElement.NamespaceURI, doc
	
	newObjNode.InnerXml = lastObjNode.InnerXml; //copy content
	
	root.InsertAfter(newObjNode, lastObjNode); //add to the bottom of the xml doc
	
	doc.Save(Application.dataPath + "/objMetaTest01.xml");
	
	Debug.Log("New Nodes Attribute: " + newObjNode.Attributes);
	Debug.Log("New Node Namespace: " + newObjNode.NamespaceURI);
	Debug.Log("New Node Name: " + newObjNode.Name);
	Debug.Log("New node is: " + newObjNode.InnerText);
}

Currently Returns

   <MetaPipeObject xmlns="DogTest">
 <FileName xmlns="">water_drop.obj</FileName>
 <Description xmlns="">Text to go here</Description>
 <Health xmlns="">10</Health>
 <Experience xmlns="">10</Experience>

The result I am wanting:

  <MetaPipeObject name="DogTest">
 <FileName>water_drop.obj</FileName>
 <Description>Text to go here</Description>
 <Health>10</Health>
 <Experience>10</Experience>


Solution

  • After much further research it turns out the solution to creating the desired XML format was two part and much simpler than the route I initially researched.

    1. Firstly I was creating the the element using the wrong method; initially I was using the CreateElement(string, string) which was, presumably, invoking the default namespace URI (hence all of the xmlns attributes being associated to each of the child tags). The method I really wanted was the CreateElement(string) which simply provides the name of the element.
    2. All that I needed to do was assign the name attribute via SetAttribute() and the issue was sorted.

    I hope this helps anyone who finds themselves in a similar situation. The NamspaceManager is another possible route I could have used and assigned a different namespace other than the default, however to resolve my issue this seemed overkill.

    Here is the snippet of code I ended up using:

    public function CreateNewNode(){
    	//Creates new node for new objects
    	//will be similar to the replace function to be written soon
    
    	var lastObjNode = root.SelectSingleNode("MetaPipeObject[last()]"); //select last MP node to add the new one behind
    	
    	var newObjNode = doc.CreateElement("MetaPipeObject");
    	
    	//Create new attribute test
    	newObjNode.SetAttribute("name", objName);
    
    	newObjNode.InnerXml = lastObjNode.InnerXml; //copy content from above listing
    	
    	newObjNode.SelectSingleNode("FileName").InnerText = fileName;
    	newObjNode.SelectSingleNode("Description").InnerText = "No Description Added Yet";
    	newObjNode.SelectSingleNode("Health").InnerText = "0";
    	newObjNode.SelectSingleNode("Experience").InnerText = "0";
    	
    	
    	root.InsertAfter(newObjNode, lastObjNode); //add to the bottom of the xml doc
    	
    	doc.Save(Application.dataPath + "/objMetaTest01.xml");
    	
    	Debug.Log("CREATED new node: " + newObjNode.GetAttribute("name"));
    
    }