Как мне обрабатывать гиперссылки 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
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
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

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