Modifying namespace in XML document programmatically

static XElement stripNS(XElement root) {
return new XElement(
root.Name.LocalName,
root.HasElements ?
root.Elements().Select(el => stripNS(el)) :
(object)root.Value
);
}
static void Main() {
var xml = XElement.Parse(@"<?xml version=""1.0"" encoding=""utf-16""?>
<ArrayOfInserts xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<insert>
<offer xmlns=""http://schema.peters.com/doc_353/1/Types"">0174587</offer>
<type2 xmlns=""http://schema.peters.com/doc_353/1/Types"">014717</type2>
<supplier xmlns=""http://schema.peters.com/doc_353/1/Types"">019172</supplier>
<id_frame xmlns=""http://schema.peters.com/doc_353/1/Types"" />
<type3 xmlns=""http://schema.peters.com/doc_353/1/Types"">
<type2 />
<main>false</main>
</type3>
<status xmlns=""http://schema.peters.com/doc_353/1/Types"">Some state</status>
</insert>
</ArrayOfInserts>");
Console.WriteLine(stripNS(xml));
}

  

I needed to validate an XML document with a given XSD document. Seems easy enough… so let’s have a look at the schema first:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://my.namespace"
elementFormDefault="qualified"
targetNamespace="http://my.namespace">
<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="firstname" type="xs:string" />
<xs:element name="lastname" type="xs:string" />
<xs:element name="age" type="xs:integer" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

The XML instance is:

<?xml version="1.0" encoding="utf-8" ?>
<customer>
<firstname>Homer</firstname>
<lastname></lastname>
<age>36</age>
</customer>

The code is straightforward:

static void Main(string[] args)
{
// Load the xml document
XDocument source = XDocument.Load(@"instance.xml");
// Load the schema
XmlSchemaSet xmlSchemaSet = new XmlSchemaSet();
xmlSchemaSet.Add(null, XmlReader.Create(@"customer.xsd"));
// Validate
try { source.Validate(xmlSchemaSet, ValidationCallback, true); }
catch (Exception ex) { Console.WriteLine(ex.Message); }
}
static void ValidationCallback(object sender,
System.Xml.Schema.ValidationEventArgs e)
{
Console.WriteLine(string.Format("[{0}] {1}", e.Severity, e.Message));
}

If you run this, no errors are thrown so it seems to validate. To be sure, let’s change the age in an invalid value:

<Age>invalid!</Age>

and test again. Well… actually, no validation error is thrown in this case either… what’s going on here?

Actually, the XML is not validated at all, because it’s not in the same namespace (http://my.namespace) as the schema definition. This is very dangerous, as we might easily get mislead by thinking that it validates because no errors are thrown. So how do we solve it?

We could ask the sender to provide the correct namespace in the XML file – this would be the best solution because then it would just work – if you try to validate the following XML:

<?xml version="1.0" encoding="utf-8" ?>
<customer xmlns="http://my.namespace">
<firstname>Homer</firstname>
<lastname></lastname>
<age>invalid</age>
</customer>

…then the validation error is thrown, because the namespaces now match:

Unfortunately, it is not always possible to change the XML file, so how can we bypass this namespace conflict? If appears that if we would change the namespace in the loaded XML document to the one we are using in our schema, the conflict is resolved. A first attempt may be:

// Load the xml document
XDocument source = XDocument.Load(@"instance.xml");
// Change namespace to reflect schema namespace
source.Root.SetAttributeValue("xmlns", "http://my.namespace");
// Load the schema
XmlSchemaSet xmlSchemaSet = new XmlSchemaSet();
xmlSchemaSet.Add(null, XmlReader.Create(@"customer.xsd"));
// Validate
try { source.Validate(xmlSchemaSet, ValidationCallback, true); }
catch (Exception ex) { Console.WriteLine(ex.Message); }

If we run this, the validation error is still not thrown, so setting the namespace attribute is not enough. The reason is that once the XDocument is loaded, every element in the tree gets prefixed with the namespace name. So we need to change them all, and so I wrote the following method that does this:

static void Main(string[] args)
{
// Load the xml document
XDocument source = XDocument.Load(@"instance.xml");
// Change namespace to reflect schema namespace
source = SetNamespace(source,"http://my.namespace");
// Load the schema
XmlSchemaSet xmlSchemaSet = new XmlSchemaSet();
xmlSchemaSet.Add(null, XmlReader.Create(@"customer.xsd"));
// Validate
try { source.Validate(xmlSchemaSet, ValidationCallback, true); }
catch (Exception ex) { Console.WriteLine(ex.Message); }
}
public static XDocument SetNamespace(XDocument source, XNamespace xNamespace)
{
foreach (XElement xElement in source.Descendants())
{
// First make sure that the xmlns-attribute is changed
xElement.SetAttributeValue("xmlns", xNamespace.NamespaceName);
// Then also prefix the name of the element with the namespace
xElement.Name = xNamespace + xElement.Name.LocalName;
}
return source;
}
static void ValidationCallback(object sender,
System.Xml.Schema.ValidationEventArgs e)
{
Console.WriteLine(string.Format("[{0}] {1}", e.Severity, e.Message));
}

The SetNameSpace method will set the corrrect namespace for each element in the XDocument. And if we run it now, the validation error is thrown again because the namespace in the XDocument has been modified and matches the schema namespace.

 
 
 
 

3 thoughts on “Modifying namespace in XML document programmatically”

  1. Janez says:

    Thanks, a working solution to a problem that took the better part of my day. :-)

  2. Jim says:

    This solution was very hard to fine…thanks so much for posting it.

  3. Mike says:

    This was very helpful and got me past some serious frustration! I was changing a child element tree to match a parent namespace, but I did not want to have the extra size of including the SetAttributeValue on all elements. My change was a change from one default namespace to another existing and prefixed one. This did the trick for me. Below are some minor adjustments that might be useful to others in some cases.

    public static XDocument SetNamespace(XDocument source, XNamespace original, XNamespace target)
    {
    //First change the element name (and namespace)
    foreach (XElement xElement in source.Descendants().Where(x => x.Name.Namespace == original))
    xElement.Name = target + xElement.Name.LocalName;

    //Second, remove the default namespace attribute.
    foreach (XElement xElement in source.Descendants().Where(x => x.Attributes().Where(y => y.Name == “xmlns”).Count() > 0))
    xElement.Attribute(“xmlns”).Remove();

    return source;
    }

Leave a Reply

Modifying namespace in XML document programmatically的更多相关文章

  1. parsing XML document from class path resource

    遇到问题:parsing XML document from class path resource [spring/resources] 解决方法:项目properties— source—remo ...

  2. eclipse错误:Unable to read workbench state. Workbench UI layout will be reset.XML document structures

    Unable to read workbench state. Workbench UI layout will be reset.XML document structures must start ...

  3. parsing XML document from class path resource [config/applicationContext.xml]; nested exception is java.io.FileNotFoundException: class path resource [config/applicationContext.xml] 解决方案

    parsing XML document from class path resource [config/applicationContext.xml]; nested exception is j ...

  4. 解决SoapFault (looks like we got no XML document)问题

    今天在调试项目的时候出现下面的错误信息: SoapFault looks like we got no XML document (D:\phpStudy\WWW\self.shop.xunmall. ...

  5. 出错: IOException parsing XML document from ServletContext resource [/cn.mgy.conig]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/cn.mgy.conig]

    错误的详细内容: 严重: StandardWrapper.Throwable org.springframework.beans.factory.BeanDefinitionStoreExceptio ...

  6. (转)EVMON_FORMAT_UE_TO_TABLES procedure - move an XML document to relational tables

    原文:https://www.ibm.com/support/knowledgecenter/zh/SSEPGG_9.8.0/com.ibm.db2.luw.sql.rtn.doc/doc/r0054 ...

  7. org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [applicationContext.xml]; nested exception is java.io.FileNotFoundException: c

    //这个是 配置文件放错了地方 org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing ...

  8. IOException parsing XML document from class path resource [WebRoot/WEB-INF/applicationContext.xml];

    parsing XML document from class path resource [applicationContext.xml]; nested exception is java.io. ...

  9. Office 365 - For security reasons DTD is prohibited in this XML document

    博客地址:http://blog.csdn.net/FoxDave 今天在测试东西的时候发现在本机运行CSOM代码或使用Office 365 PowerShell时,出现了如下错误: Connec ...

随机推荐

  1. Call to undefined function Workerman\posix_getpid()

    安装扩展 yum install php-posix

  2. jquery 判断 元素是否具有某个class

    两种方法如下: 1.hasClass(‘classname’) 2.is(‘.classname’) 例子: 1.使用is(‘.classname’)的方法 $('div').is('.redColo ...

  3. centos中单进程监控

    [root@k8s6 proc]# ps aux|grep -v PID|sort -rn -k +|head - root ? Ssl : : /usr/bin/dockerd root ? Ssl ...

  4. 手动上传图片到nginx下可访问,程序上传后访问图片报403

    1. 首先查看文件权限 2. 初步确定是服务器权限问题 2.1 解决方案一:更改文件权限 2.2 解决方案二:修改nginx运行用户 1. 首先查看文件权限 #指令如下 ls -l 2. 初步确定是服 ...

  5. 2019.02.12 bzoj5294: [Bjoi2018]二进制(线段树)

    传送门 题意简述: 给出一个长度为nnn的二进制串. 你需要支持如下操作: 修改每个位置:1变0,0变1 询问对于一个区间的子二进制串有多少满足重排之后转回十进制值为333的倍数(允许前导000). ...

  6. sql 百万级或千万级数据分页处理

    笔记来源 https://blog.csdn.net/zhenyuanjie/article/details/7778102

  7. 内置函数_zip()

    zip() zip()函数用来把多个可迭代对象中的元素压缩到一起,返回一个可迭代的zip对象,其中每个元素都是包含原来的多个可迭代对象对应位置上元素的元组,最终结果中包含的元素个数取决于所有参数序列或 ...

  8. MySQL DDL--ghost工具学习

    GHOST工作流程图: GHOST工作原理: .首先新建一张ghost表,结构与源表相同 .使用alter命令修改ghost表 3.1.模拟从库命令获取主库上该表的binlog(基于全镜像的行模式的b ...

  9. Linux pwn入门教程——格式化字符串漏洞

    本文作者:Tangerine@SAINTSEC 原文来自:https://bbs.ichunqiu.com/thread-42943-1-1.html 0×00 printf函数中的漏洞printf函 ...

  10. Docker 与 虚拟机比较

    1, 更高效的利用系统资源2,更快速的启动时间3,一致的运行环境4,持续交付(Continuous Integration)和部署(Continuous Delivery) 5, 更轻松的迁移 6,更 ...