Modifying namespace in XML document programmatically

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.

Related

Parsing large XML filesIn "C#"

Strategy patternIn "C#"

A reference architecture (part 7)In "Architecture"

3 thoughts on “Modifying namespace in XML document programmatically”

  1. Janez says: November 18, 2010 at 4:30 pm Thanks, a working solution to a problem that took the better part of my day. :-) Reply
  2. Jim says: July 3, 2013 at 4:58 pm This solution was very hard to fine…thanks so much for posting it. Reply
  3. Mike says: June 19, 2015 at 3:51 pm 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; } Reply

Leave a Reply

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

DataTable排序结果的纠正

默认情况下,即便db中某一列的值是数字,查询出来的DataSet/DataTable里,Column的类型都是String型,所以当用dataTable.Def...

21990
来自专栏互联网开发者交流社区

ASP.NET部分代码示例

12920
来自专栏王磊的博客

Net连接mysql的公共Helper类MySqlHelper.cs带MySql.Data.dll下载

MySqlHelper.cs代码如下: using System; using System.Collections.Generic; using System...

56890
来自专栏C# 编程

C#泛型类的简单创建与使用

using System; using System.Collections.Generic; using System.Linq; using System...

21900
来自专栏c#开发者

遍列schema代码

Code class XmlSchemaTraverseExample {     static void Main()     {         ...

36050
来自专栏程序员与猫

代码小目

标签: 代码片段 日常记录 日常记录的代码片段 1.使用Paralle进行并行计算累加求和的不同形式 public static int ParallelSum...

22370
来自专栏c#开发者

making Task<T> awaitable

Eduasync part 5: making Task<T> awaitable In part 3 we looked at what the C# 5 c...

34970
来自专栏跟着阿笨一起玩NET

c# 播放mp3

转载:http://www.cnblogs.com/igrl/archive/2010/03/29/1699975.html

97310
来自专栏葡萄城控件技术团队

Table-values parameter(TVP)系列之二: 利用DataTable将其作为参数传给SP

一,回顾         上一部分讲述了“在T-SQL中创建和使用TVP”,通过T-SQL建立如下的对象:         1)Tables ...

23990
来自专栏跟着阿笨一起玩NET

linq to sql 扩展方法

8900

扫码关注云+社区

领取腾讯云代金券