重命名XML名称空间前缀

时间:2009-04-04 04:18:59

标签: xml xaml xml-namespaces

我有一个XML(XAML)字符串,类似于:

<Zot xmlns="clr-namespace:A.B;assembly=A"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Zot>

Silverlight XamlReader类无法加载此字符串,它需要特定的默认命名空间:

<z:Zot 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:z="clr-namespace:A.B;assembly=A"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</z:Zot>

(WPF XamlReader没有显示这种恼人的行为)

原始格式的XML字符串以原始格式存储在数据库中。我需要将它们转换为后一种形式,并将其序列化为字符串。

有关实现此目标的最简单方法的任何建议吗?

5 个答案:

答案 0 :(得分:1)

这是我对它的破解,使用Python SAX过滤器。

import sys, string

from xml.sax import saxutils, handler, make_parser

firstElement = True

class ContentGenerator(handler.ContentHandler):

    def __init__(self, out = sys.stdout):
        handler.ContentHandler.__init__(self)
        self._out = out

    def startDocument(self):
        pass

    def startElement(self, name, attrs):
        global firstElement
        if firstElement:
            attrs = dict(attrs)
            name = "z:" + name
            if 'xmlns' in attrs:
                attrs['xmlns:z'] = attrs['xmlns']
            attrs['xmlns'] = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            firstElement = False
        elif ':' not in name:
            name = "z:" + name
        self._out.write('<' + name)
        for (name, value) in attrs.items():
            self._out.write(' %s="%s"' % (name, saxutils.escape(value)))
        self._out.write('>')

    def endElement(self, name):
        if ':' not in name:
            name = "z:" + name
        self._out.write('</%s>' % name)

    def characters(self, content):
        self._out.write(saxutils.escape(content))

    def ignorableWhitespace(self, content):
        self._out.write(content)

    def processingInstruction(self, target, data):
        self._out.write('<?%s %s?>' % (target, data))

parser = make_parser()
parser.setContentHandler(ContentGenerator())
parser.parse(sys.argv[1])

它跳转到第一个元素,使用属性进行修复,并继续在文档的其余部分中查找具有默认命名空间的所有元素。但是,您的评论表明您的文档有多个xmlns =“”属性,这意味着需要一些帮助。一般技术并不是那么糟糕,SAX管道是我们的朋友: - )。

答案 1 :(得分:0)

这可能没什么帮助,但看起来好像读者从根本上被打破了。 使用的命名空间前缀应该是无关紧要的,除非必须在属性值为QNAMS时正确绑定。

答案 2 :(得分:0)

这可能有所帮助。

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

    public class MainClass 
    {
        public static void Main() 
        {
            XNamespace nameSpace = "http://www.microsoft.com";

            XElement xBooks = new XElement(nameSpace + "Books",
              new XAttribute(XNamespace.Xmlns + "linqdev", nameSpace),
            new XElement(nameSpace + "BookParticipant"));

            XmlDocument xdoc = new XmlDocument();
            xdoc.LoadXml(xBooks.ToString());
            xdoc.Save("c:\\xmloutput.xml");

        }
    }

这将输出(来自XmlDocument xdoc,虽然实际上并不需要XmlDocument行,但我只是将它们包含在示例中以显示用途):

<linqdev:Books xmlns:linqdev="http://www.microsoft.com">
  <linqdev:BookParticipant />
</linqdev:Books> 

您可以使用一些xml读取技术从原始字符串中提取其他名称空间,然后使用上述技术创建新字符串。

答案 3 :(得分:0)

我能想到的最好的是一个样式表,它为任何具有默认xmlns属性的元素提供不同的名称空间前缀。这是在树上递归应用的,因为我的文档有多个xmlns =“...”属性。

如果它对其他人有用,这里是......虽然我的XSL技能主要基于谷歌:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:ms="urn:schemas-microsoft-com:xslt"   
    >
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>    

    <xsl:variable name="root_default_ns" select="namespace-uri(/*)" />

    <xsl:template match="node()|text()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node() | text()"/>
        </xsl:copy>
    </xsl:template>


    <xsl:template match="*">
        <xsl:param name="old_default_ns" select="$root_default_ns"/>
        <xsl:param name="new_default_ns" />

        <!-- match any element in the source default namespace -->
    <xsl:if test="namespace-uri() = $old_default_ns">
          <xsl:element name="ns_{$new_default_ns}:{local-name()}" namespace="{$old_default_ns}">
              <xsl:copy-of select="@*"/>
               <xsl:apply-templates select="node() | text()">
                   <xsl:with-param name="old_default_ns" select="$old_default_ns" />
                   <xsl:with-param name="new_default_ns" select="$new_default_ns" />
           </xsl:apply-templates>
          </xsl:element>
        </xsl:if>

        <!-- match any element with a prefix qualified namespace -->
        <xsl:if test="namespace-uri() != $old_default_ns and contains(name(), ':')"> 
          <xsl:element name="{name()}" namespace="{namespace-uri()}">
               <xsl:copy-of select="@*"/>
               <xsl:apply-templates select="node() | text()">
                   <xsl:with-param name="old_default_ns" select="$old_default_ns" />
                   <xsl:with-param name="new_default_ns" select="$new_default_ns" />
           </xsl:apply-templates>
          </xsl:element>
    </xsl:if>

        <!-- match any element *without* a prefix qualified namespace -->
        <xsl:if test="namespace-uri() != $old_default_ns and contains(name(), ':') = false"> 
      <xsl:variable name="new_ns" select="count(ancestor::*)" />

          <xsl:element name="ns_{$new_ns}:{name()}" namespace="{namespace-uri()}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" >
               <xsl:copy-of select="@*"/>
               <xsl:apply-templates select="node() | text()">
                   <xsl:with-param name="old_default_ns" select="namespace-uri()" />
                   <xsl:with-param name="new_default_ns"><xsl:value-of select="$new_ns" /></xsl:with-param>
           </xsl:apply-templates>
          </xsl:element>
        </xsl:if>

    </xsl:template>

    <!-- match root element only, and inject Silverlight namespace -->
    <xsl:template match="/*">
       <xsl:element name="ns_root:{local-name()}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" namespace="{$root_default_ns}">
          <!-- inject the Silverligth ns -->
         <xsl:variable name="dummy">
           <Dummy/>
         </xsl:variable>
         <xsl:copy-of select="ms:node-set($dummy)/*/namespace::*"/>
         <xsl:copy-of select="@*"/>
         <xsl:apply-templates select="node() | text()">
           <xsl:with-param name="old_default_ns" select="$root_default_ns" />
           <xsl:with-param name="new_default_ns">root</xsl:with-param>
         </xsl:apply-templates>
      </xsl:element>
    </xsl:template>

 </xsl:stylesheet>

不幸的是,在跳过这个箍后,我得到一个XAML字符串,它现在符合XamlReader期望的约定,但产生了一个System.ExecutionEngineException(WPF XamlReader仍然处理字符串就好了)

答案 4 :(得分:0)