Сохранение ссылок на сущности при преобразовании XML с помощью XSLT? - PullRequest
9 голосов
/ 13 мая 2011

Как сохранить ссылки на сущности при преобразовании XML с помощью XSLT (2.0)? Со всеми процессорами, которые я пробовал, сущность разрешается по умолчанию. Я могу использовать xsl:character-map для обработки символьных сущностей, но как насчет текстовых сущностей?

Например, этот XML:

<!DOCTYPE doc [
<!ENTITY so "stackoverflow">
<!ENTITY question "How can I preserve the entity reference when transforming with XSLT??">
]>
<doc>
  <text>Hello &so;!</text>
  <text>&question;</text>
</doc>

преобразовано со следующим XSLT:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

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

</xsl:stylesheet>

выдает следующий вывод:

<doc>
   <text>Hello stackoverflow!</text>
   <text>How can I preserve the entity reference when transforming with XSLT??</text>
</doc>

Вывод должен выглядеть как ввод (за исключением объявления doctype на данный момент):

<doc>
  <text>Hello &so;!</text>
  <text>&question;</text>
</doc>

Я надеюсь , что мне не нужно предварительно обрабатывать ввод, заменяя все амперсанды на &amp; (например, &amp;question;), а затем постобрабатывать вывод, заменяя все &amp; с &.

Может, это зависит от процессора? Я использую Saxon 9.

Спасибо!

Ответы [ 5 ]

5 голосов
/ 13 мая 2011

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

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:character-map name="mapEntities">
  <xsl:output-character character="&amp;" string="&amp;"/>
 </xsl:character-map>

 <xsl:variable name="vEntities" select=
 "'stackoverflow',
 'How can I preserve the entity reference when transforming with XSLT\?\?'
 "/>

 <xsl:variable name="vReplacements" select=
 "'&amp;so;', '&amp;question;'"/>

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

 <xsl:template match="/">
  <xsl:text disable-output-escaping="yes"><![CDATA[<!DOCTYPE doc [ <!ENTITY so "stackoverflow">
<!ENTITY question
"How can I preserve the entity reference when transforming with XSLT??"> ]>
]]>
  </xsl:text>

  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="text()">
  <xsl:value-of select=
  "my:multiReplace(.,
                   $vEntities,
                   $vReplacements,
                   count($vEntities)
                   )
  " disable-output-escaping="yes"/>
 </xsl:template>

 <xsl:function name="my:multiReplace">
  <xsl:param name="pText" as="xs:string"/>
  <xsl:param name="pEnts" as="xs:string*"/>
  <xsl:param name="pReps" as="xs:string*"/>
  <xsl:param name="pCount" as="xs:integer"/>

  <xsl:sequence select=
  "if($pCount > 0)
     then
      my:multiReplace(replace($pText,
                              $pEnts[1],
                              $pReps[1]
                              ),
                      subsequence($pEnts,2),
                      subsequence($pReps,2),
                      $pCount -1
                      )
      else
       $pText
  "/>
 </xsl:function>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<!DOCTYPE doc [ <!ENTITY so "stackoverflow">
<!ENTITY question
"How can I preserve the entity reference when transforming with XSLT??"> ]>
<doc>
    <text>Hello &so;!</text>
    <text>&question;</text>
</doc>

желаемый результат получен :

<!DOCTYPE doc [ <!ENTITY so "stackoverflow">
<!ENTITY question
"How can I preserve the entity reference when transforming with XSLT??"> ]>

  <doc>
      <text>Hello &so;!</text>
      <text>&question;</text>
</doc>

Примечание :

  1. Специальные (RegEx) символы в заменах должны быть экранированы.

  2. Нам нужно было разрешить DOE, что не рекомендуется, потому что это нарушает принципы архитектуры XSLT и модели обработки - другими словами, это решение - отвратительный взлом.

3 голосов
/ 28 ноября 2012

Это может быть особенно проблематично, если вы используете что-то вроде S1000D. Он использует сущности и атрибуты @boardno для связи с фигурами. Это возврат к корням SGML.

Поскольку это автоматическое поведение расширения сущности, которое является правильным, но нежелательным, мне часто приходится возвращаться к инструментам, таким как sed, awk и пакетные сценарии, для управления определенными задачами анализа данных при использовании S1000D в качестве входных данных.

ИМХО, это было бы отличным предложением об изменении одной из будущих спецификаций XSLT, согласно которой совместимый процессор принимает параметр времени выполнения, который может включать и выключать соответствующие расширения.

1 голос
/ 14 марта 2017

Я использую это решение, и оно хорошо работает:

<xsl:variable name="prolog" select="substring-before(unparsed-text(document-uri(.)),'&lt;root')"/>

<xsl:template match="/">
    <xsl:value-of select="$prolog" disable-output-escaping="yes"/>
  <xsl:apply-templates/>
</xsl:template>
1 голос
/ 13 мая 2011

Если вы используете Java-реализацию процессора XSLT 2.0 (например, Saxon 9 Java), вы можете проверить, поможет ли http://andrewjwelch.com/lexev/, вы можете предварительно обработать XML с помощью ссылок на сущности и символы таким образом, чтобы получить их.помеченные как элементы XML, которые затем можно преобразовать при необходимости.

0 голосов
/ 21 марта 2015

Вы можете сохранить узлы EntityReference в документе, используя синтаксический анализатор DOM LS с параметром "entity", установленным в значение true.http://docs.oracle.com/javase/6/docs/api/org/w3c/dom/DOMConfiguration.html

В спецификации сказано, что по умолчанию установлено значение true, но в зависимости от синтаксического анализатора это может быть false, учтите это.

Для загрузки Xerces:

DOMImplementationLS domImpl = new org.apache.xerces.dom.CoreDOMImplementationImpl();

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

DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS domImpl = (DOMImplementationLS) registry.getDOMImplementation("XML 3.0 LS 3.0"); 

Затем, чтобы загрузить документ:

// XML parser with XSD schema 
LSParser parser = domImpl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, "http://www.w3.org/2001/XMLSchema");
DOMConfiguration config = parser.getDomConfig();
config.setParameter("entities", true);
LSInput input = impl.createLSInput();
Document lDoc = parser.parse(your XML stream);

Затем,ваши сущности XML не раскрываются в DOM.

Тогда, поскольку SAXON не обрабатывает сущности, не развернутые (ошибка «Неподдерживаемый тип узла в DOM! 5»), вы не можете использовать net.sf.saxon.xpath.XPathFactoryImpl, вы должны установить XPathFactory по умолчанию для Xerces с XPathFactory.newInstance()

...