C#中的XML默认命名空间和XSLT转换问题

时间:2012-06-28 19:22:09

标签: c# xml xslt transformation xml-namespaces

在P.S.在这篇文章中你可以找到一个C#代码片段,它使用XSLT转换+ RegEx匹配来从XML文档中获取货币汇率值。请阅读

引用的内联问题

// +

...

// -

评论专栏组。

谢谢。

P.S。代码:

   string currencyCode = "RUB";
var xml = new StringReader( 
    @"<gesmes:Envelope 
        xmlns:gesmes='http://www.gesmes.org/xml/2002-08-01' 
        xmlns{{EmptyNameSpace}} ='http://www.ecb.int/vocabulary/2002-08-01/eurofxref'>
        <gesmes:subject>Reference rates</gesmes:subject>
        <gesmes:Sender>
        <gesmes:name>European Central Bank</gesmes:name>
    </gesmes:Sender>
    <Cube>
        <Cube time='2012-06-27'>
        <Cube currency='USD' rate='1.2478' />
        <Cube currency='RUB' rate='41.1252' />
        <Cube currency='ZAR' rate='10.4601' />
        </Cube>
    </Cube>
    </gesmes:Envelope>"
            .Replace("{{EmptyNameSpace}}", ":ns1")
            //+
            // I'd like to get this code working the same way as it does now
            // producing
            //
            // Result: EUR/RUB => 41.1252
            //
            // output but when the above code line will be commented
            // and the below code line uncommented
            //-
            //.Replace("{{EmptyNameSpace}}", "") 
);

var xslt = new XmlTextReader(new StringReader(
@"<xsl:stylesheet version='2.0' 
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
    xmlns:gesmes='http://www.gesmes.org/xml/2002-08-01' 
    xmlns:ns1 ='http://www.ecb.int/vocabulary/2002-08-01/eurofxref'
    >

<xsl:variable name='x1' select='gesmes:Envelope/Cube/Cube/Cube[@currency=""{0}""]' />
<xsl:template match='gesmes:Envelope'>
  [<xsl:apply-templates select='$x1' />]
</xsl:template>
<xsl:template match='Cube'>
    <xsl:value-of select='@rate' />
</xsl:template>
</xsl:stylesheet>".Replace("{0}", currencyCode)  

));

var xDoc = new XPathDocument(xml);
var xTr = new System.Xml.Xsl.XslCompiledTransform(); 
xTr.Load(xslt) ;

StringBuilder sb = new StringBuilder(); 
StringWriter writer = new StringWriter(sb); 
xTr.Transform(xDoc, null, writer);

string pattern = @"\[(?'name'[0-9]*\.[0-9]*)\]";

System.Console.WriteLine(
    "Result: EUR/{0} => {1}",
    currencyCode, 
    Regex.Match(sb.ToString(), pattern)
   .Groups["name"].Value);    

// Result: EUR/RUB => 41.1252

1 个答案:

答案 0 :(得分:0)

据我所知,通过查看该代码,您的问题是在XPath中处理默认命名空间:

  • 只要{{EmptyNameSpace}}:ns1,一切都很好 - 在您的Xml文档中,名称空间为http://www.gesmes.org/xml/2002-08-01http://www.ecb.int/vocabulary/2002-08-01/eurofxref,前缀为gesmes和{{ 1}}被定义;您的Xml文档使用来自ns1的{​​{1}}和其他名称<gesmes:Envelope>的标识符,这些标识符与任何名称空间无关。在XSLT代码中,您正在使用XPath表达式,例如(此处已缩短)gesmes,这很好,因为此表达式引用了使用<Cube>声明的名称空间中的gesmes:Envelope/Cube/Cube/Cube元素前缀,以及没有命名空间的<Envelope>元素。
  • gesmes设置为空字符串后,会发生变化:现在,<Cube>是文档的默认命名空间。因此,文档中的{{EmptyNameSpace}}元素被视为属于该命名空间。但是,您的XPath表达式仍将XPath中出现的http://www.ecb.int/vocabulary/2002-08-01/eurofxref元素视为没有命名空间 - 它们没有任何名称空间前缀,并且XPath不知道默认名称空间。

作为解决方案,您应该为XPath表达式添加名称空间前缀 - 如果<Cube>元素属于<Cube>名称空间,则XPath应如下所示:

<Cube>

(当然,XSLT中的其他XPath表达式必须相应地进行调整。)

如果您对此有任何进一步的解释,请告诉我 - 或者我是否因为我的假设走错了路。

更新:如果Xml文档具有默认命名空间,以下是XSLT的工作原理:

http://www.ecb.int/vocabulary/2002-08-01/eurofxref