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. deug的使用经验

    最基本的操作是: 1, 首先在一个java文件中设断点,然后运行,当程序走到断点处就会转到debug视图下, 2, F5键与F6键均为单步调试,F5是step into,也就是进入本行代码中执行,F6 ...

  2. 1.4eigen中的块运算

    1.4 块运算 块是矩阵或数组的一个矩形部分.块表达式既可以做左值也可以作右值.和矩阵表达式一样,块分解具有零运行时间成本,对你的程序进行优化. 1.使用块运算 最常用的块运算是.block()成员函 ...

  3. centos7配置Hadoop集群环境

    参考: https://blog.csdn.net/pucao_cug/article/details/71698903 设置免密登陆后,必须重启ssh服务 systermctl restart ss ...

  4. java jar 包加载文件问题

    场景:   A 项目是一个服务,然后部署到本地 maven 仓库里,然后 B 项目依赖 A 项目,调用  A 项目的方法,但是发现,报错,说找不到文件(config.xsv).这就很奇怪了,怎么会呢, ...

  5. Linux合上笔记本不进入休眠模式

      最近一个问题困扰了我很久,入职之前和人事说过工作中会用自己的电脑,但是人事还是坚持要给我发一个电脑,没办法,公司没有补贴,那就领了吧,索性将这个笔记本配置成了Fedora系统,用来当测试机,但是一 ...

  6. workman的学习总结

    我们知道php主要是用来做web应用的,而且平时使用的都是都是和其他的web服务器来结合使用,比如和apache,nginx和apache的时候,是作为apache的一个动态模块来加载,和nginx的 ...

  7. Linux pwn入门教程(0)——环境配置

    作者:Tangerine@SAINTSEC 0×00前言 作为一个毕业一年多的辣鸡CTF选手,一直苦于pwn题目的入门难,入了门更难的问题.本来网上关于pwn的资料就比较零散,而且经常会碰到师傅们堪比 ...

  8. Mac 下 Gradle 环境配置

    1. gradle路径的查找 然后gradle 右键 显示简介 复制下蓝色的 2. 环境变量的配置 在.bash_profile文件中,添加如下图选中内容的配置信息: 执行source .bash_p ...

  9. fatal: protocol error: bad line length character: This

    昨晚尝试搭建一个Git服务器,在搭建好服务器后,在服务器创建了一个空项目,我在本地使用git clone 拉取项目时,报了fatal: protocol error: bad line length ...

  10. 前端自动化部署方案-实践(配合shell)

    以下实例项目为vue项目,其他项目当然也雷同咯 在项目中建一个这个么脚本文件 不说了,上代码 #!/bin/sh handle=$1; env=$2; # 远程部署机 webhook # 如果用远程机 ...