2015-06-16 3 views
0

Я искал в Интернете инструмент для этого, но я не нашел его, поэтому я думал, что его должно быть просто создать. Я хочу создать XSLT, где я ввожу произвольный файл xml, и он выведет оператор select, который я могу использовать внутри базы данных Oracle для генерации входного xml.Использование XSLT для создания оператора PLSQL для вывода XML

E.g. Если я даю ему:

<?xml version="1.0"?> 
<test xmlns="dddd" xmlns:xxx="ddd222" someatt="val"> 
    <xxx:f>E</xxx:f> 
    <g>G</g> 
    <h xmlns="anotherns">H</h> 
    <zz:i xmlns:zz="yetanotherns">I</zz:i> 
</test> 

Я хочу следующий вывод:

select 
    xmlelement("test" 
    ,xmlattributes(
     'dddd' as "xmlns" 
     ,'ddd222' as "xmlns:xxx" 
     ,'val' as "someatt" 
    ) 
     ,xmlelement("xxx:f",'E') 
     ,xmlelement("g",'G') 
     ,xmlelement("h" 
     ,xmlattributes('anotherns' as "xmlns") 
     ,'H' 
    ) 
     ,xmlelement("zz:i" 
     ,xmlattributes('yetanotherns' as "xmlns:zz") 
     ,'I' 
    ) 
) 
from dual; 

Я почти всю дорогу. Я могу сделать следующий вывод с моим текущим XSLT:

select 
    xmlelement("test" 
     ,xmlattributes(
      'val' as "someatt" 
     ) 
     ,xmlelement("xxx:f",'E') 
     ,xmlelement("g",'G') 
     ,xmlelement("h",'H') 
     ,xmlelement("zz:i",'I') 
     ) 
from dual; 

Это прекрасно, за исключением того, что отсутствуют атрибуты xmlns. Проблема заключается в том, что атрибуты xmlns и xmlns: *** во входном документе не рассматриваются как обычные атрибуты и не кажутся видимыми при запуске xslt. Есть ли опция, чтобы они оставались?

XSLT У меня есть ниже:

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

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common" 

> 
<xsl:output method="text" indent="no" omit-xml-declaration="yes" /> 

<xsl:template match="/"> 


    <xsl:text>select 
    </xsl:text> 
    <xsl:apply-templates/> 
    <xsl:text> 
from dual;</xsl:text> 
</xsl:template> 


<xsl:template match="node()"> 
    <xsl:text>xmlelement("</xsl:text> 
    <xsl:value-of select='name()'/> 
    <xsl:text>"</xsl:text> 

    <!--Lots of tabs for indenting--> 
<xsl:variable name='tabs' xml:space="preserve">                                                                                                </xsl:variable> 

    <xsl:variable name='nl'><xsl:text> 
    </xsl:text><xsl:value-of select='substring($tabs,0,count(ancestor::*)+2)'/></xsl:variable> 

    <xsl:variable name='att_children' select='count(@*)'/> 
    <xsl:if test="$att_children &gt; 0"> 
     <xsl:value-of select="$nl"/> 
     <xsl:text>,xmlattributes(</xsl:text> 
     <xsl:for-each select='./@*'> 
      <xsl:value-of select="$nl"/><xsl:text> </xsl:text> 
      <xsl:if test="position() &gt; 1"><xsl:text>,</xsl:text></xsl:if> 
      <xsl:text>'</xsl:text> 
      <xsl:value-of select="."/> 
      <xsl:text>' as "</xsl:text> 
      <xsl:value-of select="name()"/> 
      <xsl:text>"</xsl:text> 
     </xsl:for-each> 
     <xsl:value-of select="$nl"/> 
     <xsl:text>)</xsl:text> 
    </xsl:if> 


    <xsl:variable name='children' select='count(*)'/> 
    <!--<xsl:value-of select='$children'/>--> 
    <xsl:choose> 
     <xsl:when test='$children=0'> 
      <xsl:text>,</xsl:text> 
      <xsl:text>'</xsl:text> 
      <xsl:value-of select='text()'/> 
      <xsl:text>'</xsl:text> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:for-each select='./*'> 
       <xsl:value-of select="$nl"/> 
       <xsl:text>,</xsl:text> 
       <xsl:apply-templates select='.'/> 
      </xsl:for-each> 
     </xsl:otherwise> 
    </xsl:choose> 
    <xsl:if test='$children &gt; 1'> 
     <xsl:value-of select="$nl"/> 
    </xsl:if> 
    <xsl:text>)</xsl:text> 
</xsl:template> 

<xsl:template match="text()|@*"> </xsl:template> 

</xsl:stylesheet> 
+1

'xmlns' не является атрибутом, это объявление пространства имен.Вы по-прежнему можете получить к ним доступ в XSLT, но вам, вероятно, нужно прочитать, как обрабатываются пространства имен XML в PLSQL/Oracle, чтобы вы знали, какой результат генерировать первым, потому что он, вероятно, может отличаться от ожидаемого вывода, который вы сейчас показываете. –

+0

@TimC кажется, что (как бы странно) это одна и та же функция для определения атрибутов или пространств имен, см., Например, http://stackoverflow.com/questions/437670/oracle-how-to-create-an-element-in-a-specific-namespace-with-xmlelement – potame

+0

Привет, да, я знаю, какой результат я хочу получить от xslt, это в вопросе. PLSQL рассматривает xmlns как обычные атрибуты, и я тестировал утверждение в вопросе, и он работает. – Robert3452

ответ

1

У меня есть только часть решения проблемы.

Он состоит из первых, считая пространства имен, чтобы быть выхода, в дополнении к атрибутам:

<xsl:variable name='att_children' select='count(@* | namespace::*[not(name() = "xml")])'/> 

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

<xsl:if test="$att_children &gt; 0"> 
    <xsl:value-of select="$nl"/> 
    <xsl:text>,xmlattributes(</xsl:text> 
    <xsl:for-each select='./@* | namespace::*[not(name() = "xml")]'> 
     <xsl:value-of select="$nl"/><xsl:text> </xsl:text> 
     <xsl:if test="position() &gt; 1"><xsl:text>,</xsl:text></xsl:if> 
     <xsl:text>'</xsl:text> 
     <xsl:value-of select="."/> 
     <xsl:text>' as "</xsl:text> 
     <xsl:choose> 
      <!-- for real attributes --> 
      <xsl:when test="self::attribute"> 
       <xsl:value-of select="name()"/> 
      </xsl:when> 
      <!-- for namespaces --> 
      <xsl:otherwise> 
      <xsl:choose> 
       <xsl:when test='name() = ""'>xmlns</xsl:when> 
       <xsl:otherwise> 
       <xsl:text>xmlns:</xsl:text> 
       <xsl:value-of select="name()"/> 
       </xsl:otherwise> 
      </xsl:choose> 
      </xsl:otherwise> 
     </xsl:choose> 
     <xsl:text>"</xsl:text> 
    </xsl:for-each> 
    <xsl:value-of select="$nl"/> 
    <xsl:text>)</xsl:text> 
</xsl:if> 

Это то, что я получаю до сих пор:

select 
    xmlelement("test" 
    ,xmlattributes(
     'dddd' as "xmlns" 
     ,'ddd222' as "xmlns:xxx" 
     ,'val' as "xmlns:someatt" 
    ) 
    ,xmlelement("xxx:f" 
     ,xmlattributes(
     'dddd' as "xmlns" 
     ,'ddd222' as "xmlns:xxx" 
    ),'E') 
    ,xmlelement("g" 
     ,xmlattributes(
     'dddd' as "xmlns" 
     ,'ddd222' as "xmlns:xxx" 
    ),'G') 
    ,xmlelement("h" 
     ,xmlattributes(
     'anotherns' as "xmlns" 
     ,'ddd222' as "xmlns:xxx" 
    ),'H') 
    ,xmlelement("zz:i" 
     ,xmlattributes(
     'dddd' as "xmlns" 
     ,'ddd222' as "xmlns:xxx" 
     ,'yetanotherns' as "xmlns:zz" 
    ),'I') 
    ) 
from dual; 

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

Я, к сожалению, не смог найти способ проверить, было ли пространство имен уже определено на элементе-предке, чтобы избежать повторения.

+0

Да, спасибо, что посмотрели на это. Пересчет пространств имен является одним из вариантов, но он становится очень сложным. Я хотел бы использовать в качестве входного документа префикс пространства имен, но есть вероятность, что тот же префикс используется на разных уровнях дерева. Также существует вероятность, что одно и то же пространство имен использует разные префиксы. Я решил собрать все пространства имен в документе вместе и вывести их на верхний уровень, но это может сломать вещи. Следуя правилу копирования, объявления, точно так же, как во входном документе, должны быть проще. – Robert3452

0

Я не уверен, что вы можете получить именно то, что вам нужно. Ось namespace:: вернет все пространства имен, действующие для узла, не обязательно, когда это пространство имен было объявлено (возможно, это был узел предка)

Что вы можете сделать, это следующее выражение, чтобы попробовать и проверить первый время префикс пространства имен и URI появится в XML

<xsl:for-each select="namespace::*[name() != 'xml']"> 
     <xsl:if test="not(../ancestor::*/namespace::*[. = current()/. and name() = current()/name()])"> 

Попробуйте XSLT (Это, возможно, потребуется некоторая работа по форматированию выхода)

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common" 

> 
<xsl:output method="text" indent="no" omit-xml-declaration="yes" /> 

<xsl:template match="/"> 


    <xsl:text>select 
    </xsl:text> 
    <xsl:apply-templates/> 
    <xsl:text> 
from dual;</xsl:text> 
</xsl:template> 


<xsl:template match="node()"> 
    <xsl:text>xmlelement("</xsl:text> 
    <xsl:value-of select='name()'/> 
    <xsl:text>"</xsl:text> 

    <!--Lots of tabs for indenting--> 
<xsl:variable name='tabs' xml:space="preserve">                                                                                                </xsl:variable> 

    <xsl:variable name='nl'><xsl:text> 
    </xsl:text><xsl:value-of select='substring($tabs,0,count(ancestor::*)+2)'/></xsl:variable> 

    <xsl:variable name="namespaces"> 
     <xsl:if test="self::*"> 
       <xsl:for-each select="namespace::*[name() != 'xml']"> 
        <xsl:if test="not(../ancestor::*/namespace::*[. = current()/. and name() = current()/name()])"> 
         <xsl:text>,'</xsl:text> 
         <xsl:value-of select="." /> 
         <xsl:text>' as "xmlns</xsl:text> 
         <xsl:if test="name() != ''"> 
          <xsl:text>:</xsl:text> 
          <xsl:value-of select="name()" /> 
         </xsl:if> 
         <xsl:text>"</xsl:text> 
        </xsl:if> 
       </xsl:for-each> 
     </xsl:if> 
    </xsl:variable> 

    <xsl:variable name='att_children' select='count(@*)'/> 
    <xsl:if test="$att_children &gt; 0 or $namespaces != ''"> 
     <xsl:value-of select="$nl"/> 
     <xsl:text>,xmlattributes(</xsl:text> 
     <xsl:value-of select="substring($namespaces, 2)" /> 
     <xsl:for-each select='./@*'> 
      <xsl:value-of select="$nl"/><xsl:text> </xsl:text> 
      <xsl:if test="position() &gt; 1 or $namespaces != ''"><xsl:text>,</xsl:text></xsl:if> 
      <xsl:text>'</xsl:text> 
      <xsl:value-of select="."/> 
      <xsl:text>' as "</xsl:text> 
      <xsl:value-of select="name()"/> 
      <xsl:text>"</xsl:text> 
     </xsl:for-each> 
     <xsl:value-of select="$nl"/> 
     <xsl:text>)</xsl:text> 
    </xsl:if> 


    <xsl:variable name='children' select='count(*)'/> 
    <!--<xsl:value-of select='$children'/>--> 
    <xsl:choose> 
     <xsl:when test='$children=0'> 
      <xsl:text>,</xsl:text> 
      <xsl:text>'</xsl:text> 
      <xsl:value-of select='text()'/> 
      <xsl:text>'</xsl:text> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:for-each select='./*'> 
       <xsl:value-of select="$nl"/> 
       <xsl:text>,</xsl:text> 
       <xsl:apply-templates select='.'/> 
      </xsl:for-each> 
     </xsl:otherwise> 
    </xsl:choose> 
    <xsl:if test='$children &gt; 1'> 
     <xsl:value-of select="$nl"/> 
    </xsl:if> 
    <xsl:text>)</xsl:text> 
</xsl:template> 

<xsl:template match="text()|@*"> </xsl:template> 

</xsl:stylesheet> 

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

Смежные вопросы