Как мне обрабатывать гиперссылки RTF с помощью Apache Tika в XSLT?

Этот вопрос является продолжением: Какие существуют методы преобразования текстовых узлов RTF в XML с использованием XSLT 2 / Saxon HE 11.3?.

После реализации решения с ответом я запустил код для большого набора данных. Во время обработки всех этих данных элемент в исходном RTF вызвал ошибку приложения.

Ошибка:

Error on line 11 column 92 of urn:from-string:  SXXP0003   Error reported by XML parser: The element type "a" must be terminated by the matching end-tag "</a>".: The element type "a" must be terminated by the matching end-tag "</a>".

Я взглянул на исходный xml, который содержал несколько кодов RTF HYPERLINK. Источник:

<SPECORMETHOD>{\rtf1\ansi\deff0\uc1\ansicpg1252\deftab720{\fonttbl{\f0\fnil\fcharset1 Arial;}{\f1\fnil\fcharset1 Times New Roman;}{\f2\fnil\fcharset1 WingDings;}}{\colortbl\red0\green0\blue0;\red255\green0\blue0;\red0\green128\blue0;\red0\green0\blue255;\red255\green255\blue0;\red255\green0\blue255;\red128\green0\blue128;\red128\green0\blue0;\red0\green255\blue0;\red0\green255\blue255;\red0\green128\blue128;\red0\green0\blue128;\red255\green255\blue255;\red192\green192\blue192;\red128\green128\blue128;\red0\green0\blue0;\red128\green128\blue0;}\wpprheadfoot1\paperw12240\paperh15840\margl720\margr720\margt720\margb720\headery720\footery720\endnhere\sectdefaultcl{\*\generator WPTools_5.17;}{\stylesheet{\s1\li0\fi0\ri0\sb0\sa0\ql\vertalt\fs20 Normal;}{\s2\li0\fi0\ri0\sb0\sa0\ql\vertalt\fs20 Default Paragraph Font;}{\s3\li0\fi0\ri0\sb0\sa0\ql\vertalt\fs20\cf3\ul\sbasedon2 Hyperlink;}}{\pard\plain\plain\f1\fs36\par\pard\plain\plain\f1\fs36\par\plain\f1\fs28\tab 10\'94Flour Tortilla\par\plain\f1\fs28\tab Caesar \f1\b\i DIP\f1\i0 : {\field{\*\fldinst{HYPERLINK "..\\\\..\\\\SAUCES\\\\Dips\\\\Dip, Caesar.doc"}}{\*\fldtitle{..\\\\..\\\\SAUCES\\\\Dips\\\\Dip, Caesar.doc}}{\fldrslt{\f1\cf3\cs103\ul\cs3 Dip, Caesar.doc\plain\f1\fs28\b}}}\par\plain\f1\fs28\tab Ripped Romaine\par\plain\f1\fs28\tab Blackened Salmon julienne\par\plain\f1\fs28\tab Shaved Red Onion\par\plain\f1\fs28\tab Julienne Tomato\par\plain\f1\fs28\tab Grated Parmesan\par\plain\f1\fs28\tab Blackening spice: {\field{\*\fldinst{HYPERLINK "..\\\\..\\\\SPICE\\\\Blackening Spice.doc"}}{\*\fldtitle{..\\\\..\\\\SPICE\\\\Blackening Spice.doc}}{\fldrslt{\f1\cf3\cs103\ul\cs3 Blackening Spice.doc\plain\f1\fs28}}}\par\pard\plain\plain\f1\fs28\par\plain\f1\fs28 Method\par\plain\f1\fs28 Procedure Text \par\pard\plain\plain\f1\fs36\par}}</SPECORMETHOD>

Для моих целей URL-адрес не будет функциональным компонентом, но ради полезности этого проекта преобразования RTF, что может потребоваться для правильной работы кодов гиперссылок или для вывода их в виде текста для справки? Один из способов справиться с этим — в XSLT перехватить элемент, найти код ГИПЕРССЫЛКИ и заменить его обычным текстом.

Желаемый вывод для гиперссылки из этого примера будет (только текст):

CAESAR DIP: ..\..\SAUCES\Dips\Dip, Caesar.doc

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

<xsl:choose>
    <xsl:when test = "string-length(SPECORMETHOD) &gt; 0">
        <rtf-as-xhtml>
            <xsl:sequence select = "tika:parse-rtf(SPECORMETHOD[string-length(.) &gt; 0])"/>
        </rtf-as-xhtml>
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select = "'[EMPTY]'"/>
    </xsl:otherwise>
</xsl:choose>

Я построил этот проект в Eclipse 2022-12 (4.26.0). Это проект Maven с использованием Apache Tika 2.7.0 и Saxon HE 11.3 с использованием Java SE 1.8. Особая благодарность Мартину Х.

Звучит так, как будто ContentHandler handler = new ToXMLContentHandler();, как я думал, вернет правильно сформированный XHTML при использовании с handler.toString() в return docBuilder.build(new StreamSource(new StringReader(handler.toString())));, не делает этого для этого ввода. Я попытаюсь изучить его, но это может занять день или два, я не знаком с деталями используемого инструмента Tika и его надежностью, или я просто не смог правильно использовать его API.

Martin Honnen 13.02.2023 20:55
Laravel с Turbo JS
Laravel с Turbo JS
Turbo - это библиотека JavaScript для упрощения создания быстрых и высокоинтерактивных веб-приложений. Она работает с помощью техники под названием...
Типы ввода HTML: Лучшие практики и советы
Типы ввода HTML: Лучшие практики и советы
HTML, или HyperText Markup Language , является стандартным языком разметки, используемым для создания веб-страниц. Типы ввода HTML - это различные...
Аутсорсинг разработки PHP для индивидуальных веб-решений
Аутсорсинг разработки PHP для индивидуальных веб-решений
Услуги PHP-разработки могут быть экономически эффективным решением для компаний, которые ищут высококачественные услуги веб-разработки по доступным...
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
Слишком много useState? Давайте useReducer!
Слишком много useState? Давайте useReducer!
Современный фронтенд похож на старую добрую веб-разработку, но с одной загвоздкой: страница в браузере так же сложна, как и бэкенд.
Узнайте, как использовать теги &lt;ul&gt; и &lt;li&gt; для создания неупорядоченных списков в HTML
Узнайте, как использовать теги <ul> и <li> для создания неупорядоченных списков в HTML
HTML предоставляет множество тегов для структурирования и организации содержимого веб-страницы. Одним из наиболее часто используемых тегов для...
0
1
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я прогнал ваш образец rtf через Tika, и предполагаемый вывод XHTML, к сожалению, не имеет правильного формата:

<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
<meta name = "X-TIKA:Parsed-By" content = "org.apache.tika.parser.DefaultParser" />
<meta name = "X-TIKA:Parsed-By" content = "org.apache.tika.parser.microsoft.rtf.RTFParser" />
<meta name = "Content-Type" content = "application/rtf" />
<title></title>
</head>
<body><p />
<p />
<p> 10”Flour Tortilla</p>
<p> Caesar <b><i>DIP</i>: <a href = "..\\..\\SAUCES\\Dips\\Dip, Caesar.doc">Dip, Caesar.doc</b><b /></b></p>
<p><b />    Ripped Romaine</p>
<p> Blackened Salmon julienne</p>
<p> Shaved Red Onion</p>
<p> Julienne Tomato</p>
<p> Grated Parmesan</p>
<p> Blackening spice: <a href = "..\\..\\SPICE\\Blackening Spice.doc">Blackening Spice.doc</a></p>
<p />
<p>Method</p>
<p>Procedure Text </p>
<p />
<p />
</body></html>

Значит ошибка во фрагменте <p> Caesar <b><i>DIP</i>: <a href = "..\\..\\SAUCES\\Dips\\Dip, Caesar.doc">Dip, Caesar.doc</b><b /></b></p>.

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

Я поднял потенциальную проблему https://issues.apache.org/jira/browse/TIKA-3972

В конце концов, с помощью ребят из Saxonica (спасибо Майклу Кею и Норму Уолшу) я нашел лучший (вероятно, во всяком случае) подход к использованию Saxon с парсером Tika; вместо использования ToXMLContentHandler() Tika и результата его метода toString(), переданного в Saxon DocumentBuilder, можно передать Saxon BuildingContentHandler парсеру Tika напрямую, чтобы получить XdmNode:

public static XdmNode parseRtfToHTML2(String rtf, Processor processor) throws IOException, SAXException, TikaException, URISyntaxException, SaxonApiException {
    DocumentBuilder docBuilder = processor.newDocumentBuilder();


    BuildingContentHandler handler = docBuilder.newBuildingContentHandler();

    AutoDetectParser parser = new AutoDetectParser();
    Metadata metadata = new Metadata();
    try (InputStream stream = new ByteArrayInputStream(rtf.getBytes("utf8"))) {
        parser.parse(stream, handler, metadata);
        return handler.getDocumentNode();//docBuilder.build(new StreamSource(new StringReader(handler.toString())));
    } catch (SaxonApiException e) {
        throw new RuntimeException(e);
    }
}

При таком подходе, по крайней мере, в коротком тесте, для примера RTF с гиперссылкой не возникает никаких ошибок, см. обновленный проект https://github.com/martin-honnen/SaxonTikaRtfTest1 для кода в более подробном контексте.

Спасибо, что посмотрели. В моем наборе данных не так много таких гиперссылок, поэтому для целесообразности я перехватил их в XSLT, когда и вернул необработанный RTF обратно в данные для ручной очистки. Среди десятков тысяч, некоторые здесь и там хороши в краткосрочной перспективе. Спасибо за ваше терпение, пока я работал над этой проблемой и учился в ваше время.

Michael Friedman 14.02.2023 00:58

@MichaelFriedman, с помощью ребят из Saxonica мне удалось найти альтернативную и, возможно, более эффективную интеграцию Tika и Saxon в встроенную функцию расширения Saxon, которая не дает никаких ошибок в вашем образце гиперссылки RTF. Смотрите отредактированный ответ.

Martin Honnen 14.02.2023 17:31

Я протестировал новый код на полном наборе данных и могу подтвердить, что это решает проблему. Результат xhtml для одной из этих гиперссылок: <p> Caesar Dressing: <a href = "..\\..\\..\\DRESSING\\Emulsified\\Caesar Dressing.doc">Caesar Dressing.doc</a> </p> Я ценю вашу настойчивость и эксперименты, а также помощь от людей из Saxonica. Форматирование RTF представляет большую проблему для данных, с которыми я работаю, и я был удивлен, не обнаружив дополнительных возможностей XML/XSLT. Это имеет большое значение.

Michael Friedman 15.02.2023 18:47

Другие вопросы по теме