XSLT: помогите мне исправить несколько тегов тела - PullRequest
1 голос
/ 23 марта 2009

Мне нужно постобработать некоторый HTML с плохой структурой - например,

<html>
<body>...</body>
<body>...</body>
</html>

Как лучше всего преобразовать этот HTML-код, чтобы содержимое второго тела отображалось внутри первого, кроме, конечно, дополнительного тега тела? Я не хочу манипулировать чем-либо еще с этим правилом.

Я думал о сопоставлении тега html и обработке его оттуда с помощью явных вызовов apply-templates, но мне это кажется немного неаккуратным. Я знаю, как сопоставить паразитные тела («body [position ()> 1]»)), но мне хотелось бы получить несколько идей о том, как лучше всего написать преобразование.

Редактировать: мне нужно применить другие шаблоны к дочерним элементам всех этих элементов, поэтому простая копия не будет работать.

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

Редактировать 2: важно сохранить дочерние элементы второго элемента тела в приведенном выше примере. Они должны быть потомками первого тега body в выходных данных, в конце дочерних узлов первого тега body.

Редактировать 3: Вот некоторые иллюстративные ввод / вывод (не проверены на достоверность):

<html>
  <!-- Look at my comments -->
  <head>
    <title>My title!</title>
    <!-- Commentary -->
  </head>
  <body>
     <p>Something <b>bold</b></p>
  </body>
  <body>
     <!-- heh -->
     <p>Some bozo put my parent in here.</p>
  </body>
  <body>
     <p>More stuff here</p>
  </body>
</html>

должно быть:

<html>
  <!-- Look at my comments -->
  <head>
    <title>My title!</title>
    <!-- Commentary -->
  </head>
  <body>
     <p>Something <b>bold</b></p>
     <!-- heh -->
     <p>Some bozo put my parent in here.</p>
     <p>More stuff here</p>
  </body>
</html>

Ответы [ 6 ]

2 голосов
/ 23 марта 2009

Добавьте эти шаблоны к преобразованию идентичности:

<xsl:template match="/html/body[1]">
   <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
      <xsl:apply-templates select="/html/body[2]/node() | /html/body[2]/@*"/>
   </xsl:copy>
</xsl:template>

<xsl:template match="/html/body"/>

Edit:

Чтобы пояснить это, вместо body[2] в вышеприведенном вы можете использовать body[position() != 1]. Это будет обрабатывать случай, когда ваш вход имеет более двух body элементов.

1 голос
/ 23 марта 2009

Я думаю, что @Keltex означало, что вы должны раздеться

</body>\s*<body>

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

Я бы так и сделал.

(Предполагается, что у нескольких тегов тела нет содержимого между ними.)

РЕДАКТИРОВАТЬ: Это не приведет к удалению содержимого тегов тела. Обратите внимание, что вы удаляете что-нибудь из закрывающего тега body и открывающего . Это оставит на месте начальные и конечные теги. Другими словами, с таким вводом

<body>
    good stuff
</body>
<body>
    more good stuff
</body>

вы нацеливаетесь на эти два тега посередине. Удаление их приведет к единственному непрерывному телу:

<body>
    good stuff
    more good stuff
</body>
1 голос
/ 23 марта 2009

Возможно, это ближе к тому, что вы хотели:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="2.0" exclude-result-prefixes="xsl">
<xsl:output indent="yes" method="html"/>

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

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

<!-- Matches on the first 'body' tag -->
<xsl:template match="body[1]">
    <xsl:copy>
        <!-- apply=templates the children of all the body tags -->
        <xsl:apply-templates select="//body/node()"/>
    </xsl:copy>
</xsl:template>

<!-- Skip processing on the subsequent body tags 
     (their children are still processed however)   -->
<xsl:template match="body"/>

</xsl:stylesheet>

Здесь используется популярная структура push для шаблонов, поэтому вы можете найти ее более гибкой.

1 голос
/ 23 марта 2009

Если ваш входной HTML является правильно сформированным XML, то этот шаблон XSLT сделает это:

<xsl:template match="/">
  <body>
    <xsl:copy-of select="//body/node()" />
  </body>
</xsl:template>

(в данном примере я не заботился об узле <html>, поскольку это тривиально.)

Более гибкий вариант вышеуказанного (согласно запросу ОП)

<!-- explicitly catching the initial html circumvents built-in templates -->
<xsl:template match="/html">
  <xsl:copy>
    <xsl:apply-templates />
  </xsl:copy>
</xsl:template>

<!-- copy everything that is not processed otherwise -->
<xsl:template match="@*|node()|processing-instruction()">
  <xsl:copy-of select="." />
</xsl:template>

<!-- matches any "body" node, but produces output only for the first -->
<xsl:template match="body">
  <xsl:if test="not(preceding-sibling::body)">
    <xsl:copy>
      <xsl:apply-templates select="//body/@*|//body/node()" />
    </xsl:copy>
  </xsl:if>
</xsl:template>

<!-- you can add more of these specific templates, as needed -->
<xsl:template match="body//a">
  <b>
    <xsl:copy-of select="." />
  </b>
</xsl:template>

Этот вход:

<html>
  <head><title>Foo!</title></head>
  <?dummy processing instruction?>
  <body foo="bar">...<a href="foo">asd</a><!-- comment --></body>
  <body>...contents of body#2...</body>
</html>

возвращает мне этот результат (пробел и отступы изменены для удобства чтения):

<html>
  <head><title>Foo!</title></head>
  <?dummy processing instruction?>
  <body foo="bar">
    ...
    <b><a href="foo">asd</a></b>
    <!-- comment -->
    ...contents of body#2...
  </body>
</html>
1 голос
/ 23 марта 2009

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

Вместо этого вам лучше починить сломанный HTML в его источнике, наличие нескольких тегов тела звучит как серьезное недопонимание где-то.

0 голосов
/ 23 марта 2009

Если HTML-код испортился, то я не хотел бы предположить, что HTML-код достаточно сформирован, чтобы использовать xlst Вы можете просто использовать регулярные выражения, чтобы найти

<body>(whitespace)</body>

и удалите его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...