Oracle XMLQuery, вставляющий нежелательное пространство имен

Oracle 11.2

Ниже приведена сокращенная версия XMLQuery, которая выполняется в столбце XMLType. Когда я запускаю запрос, который просто анализирует и воссоздает сохраненный XML, в дочерние элементы родителя вставляются нежелательные области имен по умолчанию и tsip. Обратите внимание, что пространство имен tsxm не вставлено, это связано с тем, что оно не равно пространству имен по умолчанию. Этот запрос ничего не делает и может быть легко переписан, но реальный (гораздо больший) запрос использует эту же методологию, поэтому поэтому я размещение вопроса в этом формате.

создать таблицу:

CREATE TABLE XML_DOCUMENT_TMP
(
  DOCUMENT_ID   NUMBER(12)                      NOT NULL,
  XML_DATA      SYS.XMLTYPE                     NOT NULL,
  CREATED_DATE  TIMESTAMP(6)                    NOT NULL
);

Вставьте некоторые данные (которые должны иметь пространства имен как есть):

insert into XML_DOCUMENT_TMP
(document_id,created_date,xml_data)
values(1,sysdate, 
'<patent  xmlns="http://schemas.thomson.com/ts/20041221/tsip" 
    xmlns:tsip="http://schemas.thomson.com/ts/20041221/tsip" 
    xmlns:tsxm="http://schemas.thomson.com/ts/20041221/tsxm"  
    tsip:action="replace" tsip:cc="CA" tsip:se="2715340" tsip:ki="C">
    <accessions tsip:action="replace">
        <accession tsip:src="wila" tsip:type="key">CA-2715340-C</accession>
        <accession tsip:src="tscm" tsip:type="tscmKey">CA-2715340-C-20150804</accession>
    </accessions>
    <claimed tsip:action="replace">
    <    claimsTsxm tsip:lang="en">
            <tsxm:heading tsxm:align="left">We Claim:</tsxm:heading>
            <claimTsxm tsip:no="1" tsxm:num="1" tsip:type="main">1.  power.       </claimTsxm>
      </claimsTsxm>
  </claimed>
</patent>
');

Запустите XMLQuery:

Обратите внимание на необходимость использования подстановочного символа пространства имен здесь

WITH tmpTable AS (
SELECT * FROM XML_DOCUMENT_TMP cm )
SELECT tt.xml_data ,
XMLQuery('declare default element namespace  "http://schemas.thomson.com/ts/20041221/tsip";
  declare namespace  tsip="http://schemas.thomson.com/ts/20041221/tsip";
  declare namespace  tsxm="http://schemas.thomson.com/ts/20041221/tsxm"; 


  return          
  <patent>{$m/*:patent/@*}
  {
    for $i in $m/*:patent/*
        return    $i
  }
  </patent>' 
        PASSING tt.xml_data as "m"   RETURNING CONTENT) newXml 
 FROM tmpTable tt
 WHERE tt.document_id in (1);

Возврат:

<patent xmlns="http://schemas.thomson.com/ts/20041221/tsip" xmlns:tsip="http://schemas.thomson.com/ts/20041221/tsip" tsip:action="replace" tsip:cc="CA" tsip:se="2715340" tsip:ki="C">
    <accessions xmlns="http://schemas.thomson.com/ts/20041221/tsip" xmlns:tsip="http://schemas.thomson.com/ts/20041221/tsip" tsip:action="replace">
        <accession tsip:src="wila" tsip:type="key">CA-2715340-C</accession>
        <accession tsip:src="tscm" tsip:type="tscmKey">CA-2715340-C-20150804</accession>
    </accessions>
    <claimed xmlns="http://schemas.thomson.com/ts/20041221/tsip" xmlns:tsip="http://schemas.thomson.com/ts/20041221/tsip" tsip:action="replace">
        <claimsTsxm tsip:lang="en">
            <tsxm:heading xmlns:tsxm="http://schemas.thomson.com/ts/20041221/tsip" tsxm:align="left">We Claim:</tsxm:heading>
            <claimTsxm tsip:no="1" xmlns:tsxm="http://schemas.thomson.com/ts/20041221/tsip" tsxm:num="1" tsip:type="main">1.  power.</claimTsxm>
        </claimsTsxm>
</claimed>

Как избавиться от нежелательных пространств имен, созданных в элементах доступа и заявленных элементах. Любые предложения оценили.

Ответ 1

Если вы играете с различными значениями пространств имен, вы можете видеть, что в то время как верхний уровень <patent> пространства имен объявляются и включаются из-за заявлений, которые вы делаете, на уровне дочерних элементов эта информация не используется в как вы ожидаете.

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

В других статьях объясняется, что то, что вы пытаетесь сделать, это "разбор" данных, а также "Извлечь" его, что верно в некоторой степени, и поэтому XSLT - это правильный инструмент, а не XQuery.

Одна внешняя ссылка, которую я нашел, которая имеет способ XQuery для удаления пространств имен и поэтому возвращает вам "необработанный" XML здесь.

Применение этого кода к вашему XQuery привело меня к:

SELECT xmlquery('xquery version "1.0"; (: :)
             declare default element namespace 
                        "http://www.somewherein.uk/ns/1.0"; (: :)

             declare function local:strip-namespace($inputRequest  as element()) as element()
             {
                element {xs:QName(local-name($inputRequest ))}
                {
                  for $child in $inputRequest /(@*,node())
                    return
                      if ($child instance of element())
                      then local:strip-namespace($child)
                      else $child
                }
             }; (: :)

             <patent>
             {
             for $s in /*:patent/*
              return local:strip-namespace($s)
             }
             </patent>' 
             PASSING cmf.XML_DATA 
             RETURNING content)
FROM XML_DOCUMENT_TMP cmf WHERE cmf.DOCUMENT_ID=1

Дальнейшее редактирование привело меня к приведенному ниже, что, я думаю, было тем, чем вы были (пространства имен, определенные на уровне patent)

SELECT xmlquery('xquery version "1.0"; (: :)
             declare default element namespace 
                        "http://www.somewherein.uk/ns/1.0"; (: :)

             declare function local:strip-namespace($inputRequest as element()) as element()
             {
                element {fn:name($inputRequest)}
                {
                  for $child in $inputRequest /(@*,node())
                    return
                      if ($child instance of element())
                      then local:strip-namespace($child)
                      else $child
                }
             }; (: :)

             <patent>
             {
             for $s in /(*:patent, node())
              return local:strip-namespace($s)
             }
             </patent>' 
             PASSING cmf.XML_DATA 
             RETURNING content)
FROM XML_DOCUMENT_TMP cmf WHERE cmf.DOCUMENT_ID=1;

Как отмечалось ниже, это привело к некоторому дублированию кода цикла из-за некоторых проблем в XPath. Это также означало, что пространство имен txsm было объявлено пару раз; XQuery объявляет его "в первый раз", когда он сталкивается с пространством имён, используемым им, когда он ходит по этой ветке дерева, что означает, что если есть братья и сестры, которые используют ns, то он будет объявлен несколько раз. Перемещая явное размещение объявления обратно в родительский node, мы можем это исключить.

SELECT xmlquery('xquery version "1.0"; (: :)
             declare default element namespace  "http://schemas.thomson.com/ts/20041221/tsip"; (: :)
             declare namespace  tsip="http://schemas.thomson.com/ts/20041221/tsip"; (: :)
             declare namespace  tsxm="http://schemas.thomson.com/ts/20041221/tsxm"; (: :)

             declare function local:strip-namespace($inputRequest as element()) as element()
             {
                element {fn:name($inputRequest)}
                {
                  for $child in $inputRequest /(@*,node())
                    return
                      if ($child instance of element())
                      then local:strip-namespace($child)
                      else $child
                }
             }; (: :)

             <patent xmlns:tsxm="http://schemas.thomson.com/ts/20041221/tsxm" xmlns:tsip="http://schemas.thomson.com/ts/20041221/tsip">
             {
             for $s in /*:patent/*
              return local:strip-namespace($s)
             }
             </patent>' 
             PASSING cmf.XML_DATA
             RETURNING content)
FROM XML_DOCUMENT_TMP cmf WHERE cmf.DOCUMENT_ID=1;

Ответ 2

Внесение изменений в решение @Graham Nicol 2, это, похоже, дает правильный ответ:

SELECT xmlquery('xquery version "1.0";
         declare default element namespace 
                    "http://schemas.thomson.com/ts/20041221/tsip";

         declare function local:strip-namespace($inputRequest as element()) as element()
         {
            element {fn:name($inputRequest)}
            {
              for $child in $inputRequest /(@*,node())
                return
                  if ($child instance of element())
                  then local:strip-namespace($child)
                  else $child
            }
         }; 


         <patent>{/*:patent/@*}
         {
         for $s in /*:patent/*
          return local:strip-namespace($s)
        }
         </patent>
         ' 
         PASSING cmf.XML_DATA 
         RETURNING content)
FROM XML_DOCUMENT_TMP cmf WHERE cmf.DOCUMENT_ID=1;