views:

1024

answers:

5

I have an XML (XAML) string that looks something like:

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

The Silverlight XamlReader class is unable to load this string, it needs a particular default namespace:

<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"&gt;
</z:Zot>

(The WPF XamlReader doesn't show this annoying behaviour)

The XML strings in the original format are stored in a database in the original form. I need to way to transform them into the latter form, and serialize it as a string.

Any suggestions on the simplest way to achieve this?

A: 

This may be not of much help, but it sure seems like said reader is fundamentally broken. Namespace prefix used should be inconsequential, except that it has to be properly bound if attribute values are QNAmes.

StaxMan
A: 

This may help.

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

        }
    }

This would output (from the XmlDocument xdoc, though the XmlDocument lines aren't actually required, I just included them in the example to show use):

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

You could use some xml read techniques to pull out your other namespaces from your original string and then use the technique above to create your new string.

Joshua
A: 

The best I could come up with was a stylesheet that gives any element with a default xmlns attribute a different namespace prefix. This is applied recursively down the tree, since my documents have multiple xmlns="..." attributes.

In case it is useful to anyone else, here it is ... though my XSL skills are mainly based on 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>

Unfortunately, after jumping through this hoop I get a XAML string which now conforms to the conventions the XamlReader expects, but produces a System.ExecutionEngineException instead (the WPF XamlReader still processes the string just fine)

Rob Walker
+1  A: 

Here's my crack at it, using a Python SAX filter.

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])

It jumps on the first element, mucks around with the attributes, and continues looking for all of the elements with the default namespace in the rest of the document. However, your comment that your documents have multiple xmlns="" attributes sprinkled around means this will need some help. The general technique isn't so bad, SAX pipelines are our friends :-).

Joel
A: 

Hi,

Maybe you can use this on: http://www.cnblogs.com/sheva/archive/2006/08/28/488915.html

Best regards,

Eric Gehring www.softex.nl

Thanks for the link -- unfortunately Silverlight doesn't honour the assembly:XmlnsDefinition attribute (though it still compiles)
Rob Walker