Переименовать префиксы пространства имен XML - PullRequest
1 голос
/ 04 апреля 2009

У меня есть строка 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 ]

1 голос
/ 05 апреля 2009

Вот мой взлом, используя фильтр 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-конвейеры - наши друзья: -).

0 голосов
/ 15 апреля 2009

Может быть, вы можете использовать это на: http://www.cnblogs.com/sheva/archive/2006/08/28/488915.html

0 голосов
/ 04 апреля 2009

Это может помочь.

    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");

        }
    }

Это выведет (из xml Document XmlDocument, хотя строки XmlDocument фактически не требуются, я просто включил их в пример, чтобы показать использование):

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

Вы можете использовать некоторые методы чтения XML, чтобы извлечь другие пространства имен из исходной строки, а затем использовать вышеописанную технику для создания новой строки.

0 голосов
/ 05 апреля 2009

Лучшее, что я мог придумать, это таблица стилей, которая дает любому элементу с атрибутом по умолчанию xmlns другой префикс пространства имен. Это применяется рекурсивно вниз по дереву, так как мои документы имеют несколько атрибутов xmlns = "...".

В случае, если это пригодится кому-то еще, вот оно ... хотя мои навыки XSL в основном основаны на Google:

<?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 все еще обрабатывает строку очень хорошо)

0 голосов
/ 04 апреля 2009

Это может не сильно помочь, но похоже, что указанный читатель существенно сломан. Используемый префикс пространства имен должен быть несущественным, за исключением того, что он должен быть правильно связан, если значения атрибута являются QNAmes.

...