Хобрук: Ваш путь к мастерству в программировании

Замена новой строки в атрибутах XML на XSLT

Мне нужен XSLT (или что-то - см. Ниже), чтобы заменить символы новой строки во всех атрибутах альтернативным символом.

Мне приходится обрабатывать устаревший XML, который хранит все данные как атрибуты и использует новые строки для выражения количества элементов. Например:

<sample>
    <p att="John
    Paul
    Ringo"></p>
</sample>

Эти новые строки заменяются пробелами, когда я анализирую файл на Java (в соответствии со спецификацией XML), однако я хочу рассматривать их как список, поэтому такое поведение не особенно полезно.

Мое «решение» заключалось в том, чтобы использовать XSLT для замены всех символов новой строки во всех атрибутах каким-либо другим разделителем, но я ничего не знаю о XSLT. Все примеры, которые я видел до сих пор, были либо очень конкретными, либо заменяли содержимое узла вместо значений атрибутов.

Я пробовал replace() XSLT 2.0, но мне трудно собрать все вместе.

XSLT - это вообще правильное решение? С помощью XSLT ниже:

<xsl:template match="sample/*">
    <xsl:for-each select="@*">
        <xsl:value-of select="replace(current(), '\n', '|')"/>
    </xsl:for-each>
</xsl:template>

примененный к образцу XML, с помощью Saxon выводит следующее:

John Paul Ringo

Очевидно, мне нужен не этот формат - я просто поэкспериментирую с replace() - но нормализованы ли уже символы новой строки к тому времени, когда мы перейдем к обработке XSLT? Если да, то есть ли другие способы синтаксического анализа этих значений как записи с использованием синтаксического анализатора Java? Пока что я использовал только JAXB.

02.07.2013

  • У меня очень неприятное ощущение, что мне, возможно, придется надеть резиновые перчатки и реализовать грязное регулярное выражение в строке XML перед синтаксическим анализом. К сожалению, я не могу контролировать создаваемый XML. 02.07.2013
  • На самом деле нет, это было бы слишком ужасно, чтобы думать. 02.07.2013
  • Если пробелы в значениях атрибутов семантически значимы, значит, вы не имеете дело с XML, и для его обработки вам потребуется не-XML инструмент. Согласно спецификации все символы новой строки в значении атрибута должны быть преобразованы в пробелы синтаксическим анализатором, и если вам нужен символ новой строки в значении, которое вы видите после синтаксического анализа, он должен быть экранирован как ссылка на символ (&#10;) 02.07.2013
  • Я с тобой согласен. XML экспортируется из приложения, которое останется безымянным. Это не полностью ошибка приложения, хотя заполнение всех данных атрибутами, возможно, является несколько сомнительным подходом. Я подозреваю, что пользователи обошли недостаток мощности 1: M для этого конкретного поля, используя символы новой строки, которые приложение слепо экспортировало в XML без подделки. 02.07.2013
  • Я мог бы провести небольшое исследование любых библиотек Java, которые предназначены для сомнительного XML - это не может быть изолированным экземпляром, поэтому я уверен, что кто-то там написал намеренно свободный / прощающий синтаксический анализатор. 02.07.2013

Ответы:


1

Кажется, это сложно сделать. Как я обнаружил в Разрешены ли разрывы строк в значениях атрибутов XML? - символ новой строки в атрибуте действителен, но синтаксический анализатор XML нормализует его (https://stackoverflow.com/a/8188290/1324394), поэтому он, вероятно, утерян перед обработкой (и, следовательно, перед заменой).

02.07.2013
  • Я тоже это видел, но я надеялся, что они все еще будут там для некоторых исправлений XSLT. С тех пор я нашел jdom.org, который обходит проблему, не претендуя на роль анализатора XML, что, по-видимому, облегчает ее о необходимости соответствовать спецификации XML. Сейчас попробую ... 02.07.2013
  • Просто подумав вслух, вы могли бы сделать что-то вроде этого replace(/data/@value, '\s{2,10}','|') - это не совсем правильно, потому что предполагается, что вместо новой строки будет более одного пробела, но это может сделать работу. 02.07.2013
  • @ JirkaŠ. нет, это не сработает, потому что синтаксический анализатор XML сжимает все последовательные пробелы в значениях атрибутов до одного пробела до того, как данные дойдут до модели данных XPath. 02.07.2013
  • Я боялся этого, но попробовал в Альтове, и это сработало. Может быть, это просто альтовская специфика. 02.07.2013
  • Ах, я вижу, что пропустил ключевое предложение в спецификации: все атрибуты, для которых никакое объявление не было прочитано ДОЛЖНО обрабатываться непроверяющим процессором, как если бы оно было объявлено CDATA. - поэтому, если у вас нет DTD, синтаксический анализатор заменит символы новой строки пробелами, но не сворачивает последовательные пробелы до одного пробела. 02.07.2013
  • Re: расследование JDom - несмотря на явное указание на обратное на stackoverflow.com/a/10439549/1239406, атрибуты нормализованы от Xerces еще до того, как попасть в JDom. 02.07.2013

  • 2

    Я решил (иш) проблему, предварительно обработав XML с помощью JSoup (что является намеком на комментарий @Ian Roberts о синтаксическом анализе XML с помощью инструмента, отличного от XML). JSoup разработан (или был) разработан для HTML-документов, однако хорошо работает в этом контексте.

    Мой код выглядит следующим образом:

    @Test
    public void verifyNewlineEscaping() {
        final List<Node> nodes = Parser.parseXmlFragment(FileUtils.readFileToString(sourcePath.toFile(), "UTF-8"), "");
    
        fixAttributeNewlines(nodes);
    
        // Reconstruct XML
        StringBuilder output = new StringBuilder();
        for (Node node : nodes) {
            output.append(node.toString());
        }
    
        // Print cleansed output to stdout
        System.out.println(output);
    }
    
    /**
     * Replace newlines and surrounding whitespace in XML attributes with an alternative delimiter in
     * order to avoid whitespace normalisation converting newlines to a single space.
     * 
     * <p>
     * This is useful if newlines which have semantic value have been incorrectly inserted into
     * attribute values.
     * </p>
     * 
     * @param nodes nodes to update
     */
    private static void fixAttributeNewlines(final List<Node> nodes) {
    
        /*
         * Recursively iterate over all attributes in all nodes in the XML document, performing
         * attribute string replacement
         */
        for (final Node node : nodes) {
            final List<Attribute> attributes = node.attributes().asList();
    
            for (final Attribute attribute : attributes) {
    
                // JSoup reports whitespace as attributes
                if (!StringUtils.isWhitespace(attribute.getValue())) {
                    attribute.setValue(attribute.getValue().replaceAll("\\s*\r?\n\\s*", "|"));
                }
            }
    
            // Recursively process child nodes
            if (!node.childNodes().isEmpty()) {
                fixAttributeNewlines(node.childNodes());
            }
        }
    }
    

    Для образца XML в моем вопросе результат этого метода:

    <sample> 
        <p att="John|Paul|Ringo"></p> 
    </sample>
    

    Обратите внимание, что я не использую &#10;, потому что JSoup довольно бдителен в экранировании своего символа и избегает всего в значениях атрибутов. Он также заменяет существующие числовые ссылки на сущности их эквивалентом в UTF-8, поэтому время покажет, является ли это приемлемым решением.

    03.07.2013
  • Обратите внимание, что недостатком использования JSoup является то, что в настоящее время он преобразует имена атрибутов в нижний регистр. Об этом свидетельствует открытая ошибка. 03.07.2013

  • 3

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

    Я думаю, что в некоторых XML-парсерах есть возможность подавлять нормализацию значений атрибутов. Если у вас нет доступа к такому синтаксическому анализатору, я думаю, что выполнение текстовой замены (\ r? \ N) на &#x0A; перед синтаксическим анализом может быть вашим лучшим выходом. Новые строки, которые экранированы таким образом, не попадают в раздел при нормализации значения атрибута.

    02.07.2013
  • Спасибо, Майкл. После разумного количества копаний я придумываю пробелы, пытаясь найти синтаксический анализатор на основе Java, который позволяет подавить нормализацию значений атрибутов. Текстовая замена сложна, так как я не могу контролировать создаваемый XML. Это означает, что я не могу ограничить замену значениями атрибутов. 03.07.2013
  • Новые материалы

    ВЫ РЕГРЕСС ЭТО?
    Чтобы понять, когда использовать регрессионный анализ, мы должны сначала понять, что именно он делает. Вот простой ответ, который появляется, когда вы используете Google: Регрессионный..

    Не зря же это называют интеллектом
    Стек — C#, Oracle Опыт — 4 года Работа — Разведывательный корпус Мне пора служить Может быть, я немного приукрашиваю себя, но там, где я живу, есть обязательная военная служба на 3..

    LeetCode Проблема 41. Первый пропущенный положительный результат
    LeetCode Проблема 41. Первый пропущенный положительный результат Учитывая несортированный массив целых чисел, найдите наименьшее пропущенное положительное целое число. Пример 1: Input:..

    Расистский и сексистский робот, обученный в Интернете
    Его ИИ основан на предвзятых данных, которые создают предрассудки. Он словно переходит из одного эпизода в другой из серии Черное зеркало , а вместо этого представляет собой хронику..

    Управление состоянием в микрофронтендах
    Стратегии бесперебойного сотрудничества Микро-фронтенды — это быстро растущая тенденция в сфере фронтенда, гарантирующая, что удовольствие не ограничивается исключительно бэкэнд-системами..

    Декларативное и функциональное программирование в стиле LINQ с использованием JavaScript с использованием каррирования и генератора ...
    LINQ - одна из лучших функций C #, которая обеспечивает элегантный способ написания кода декларативного и функционального стиля, который легко читать и понимать. Благодаря таким функциям ES6,..

    Структуры данных в C ++ - Часть 1
    Реализация общих структур данных в C ++ C ++ - это расширение языка программирования C, которое поддерживает создание классов, поэтому оно известно как C с классами . Он используется для..