Исправление неэкранированных объектов XML в Java с помощью Regex? - PullRequest
5 голосов
/ 11 июля 2011

У меня есть какой-то плохо отформатированный XML, который я должен проанализировать.Устранить проблему в восходящем направлении невозможно.

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

Если &amp; уже есть, я не хочу менять его на &amp;amp;.В общем, если какая-то правильно сформированная сущность уже существует, я не хочу ее разрушать.Я не думаю, что вообще возможно знать все сущности, которые могут появиться в любом конкретном XML-документе, поэтому я хочу найти решение, в котором сохраняется что-то вроде &<characters>;.

Где <characters>некоторый набор символов, определяющих сущность между начальным & и заключительным ;.В частности, < и > являются литералами , а не , которые в противном случае обозначали бы элемент XML.

Теперь, при разборе, если я вижу &<characters>, я не знаю,Я столкнусь с ;, (пробел), концом строки или другим &.Поэтому я думаю, что я должен помнить <characters>, когда я смотрю вперед, чтобы найти персонажа, который скажет мне, что делать с оригинальным &.

Я думаю, что мне нужна силаЧтобы сделать это, нажмите «Вниз», и я не думаю, что конечный автомат будет работать из-за того, что я считаю требованием к памяти - это правильно? Если мне нужен КПК, то для вызова регулярного выражения в вызовеString.replaceAll(String, String) не будет работать.Или существует регулярное выражение Java, которое может решить эту проблему?

Помните: в каждой строке может быть несколько замен.

(мне известен этот вопрос , ноон не дает ответа, который я ищу.)

Ответы [ 5 ]

8 голосов
/ 12 июля 2011

Вот регулярное выражение, которое вы ищете: &([^;\\W]*([^;\\w]|$)), и соответствующая замещающая строка будет &amp;$1.Он совпадает с &, за которым следует ноль или более не точек с запятой или разрывов слов (необходимо, чтобы ноль соответствовал отдельному амперсанду), за которым следует разрыв слова, равный не точка с запятой (или конец строки).Группа захвата позволяет выполнить замену с &amp;, который вы ищете.

Вот пример кода, использующего его:

String s = "&amp; & &nsbp; &tc., &tc. &tc";
final String regex = "&([^;\\W]*([^;\\w]|$))";
final String replacement = "&amp;$1";
final String t = s.replaceAll(regex, replacement);

После запуска в песочнице яполучите следующий результат для t:

&amp; &amp; &nsbp; &amp;tc., &amp;tc. &amp;tc

Как видите, исходные &amp; и &nbsp; остаются без изменений.Однако, если вы попробуете это с «&&», вы получите &amp;&, а если вы попробуете это с «&&&», вы получите &amp;&&amp;, что я воспринимаю как признак проблемы прогнозирования, на которую вы ссылались.Однако, если вы замените строку:

final String t = s.replaceAll(regex, replacement);

на:

final String t = s.replaceAll(regex, replacement).replaceAll(regex, replacement);

Она будет работать со всеми этими строками и любыми другими, которые я мог придумать.(В готовом продукте вы, вероятно, написали бы одну подпрограмму, которая выполняла бы этот двойной replaceAll вызов.)

5 голосов
/ 15 апреля 2014

Я думаю, вы также можете использовать упреждающий просмотр, чтобы увидеть, сопровождаются ли символы & символами и точкой с запятой (например, &(?!\w+;)).Вот пример:

import java.util.*;
import java.util.regex.*;

public class HelloWorld{
    private static final Pattern UNESCAPED_AMPERSAND =
        Pattern.compile("&(?!(#\\d+|\\w+);)");
     public static void main(String []args){
        for (String s : Arrays.asList(
            "http://www.example.com/?a=1&b=2&amp;c=3/",
            "Three in a row: &amp;&&amp;",
            "&lt; is <, &gt; is >, &apos; is ', etc."
        )) {
            System.out.println(
                UNESCAPED_AMPERSAND.matcher(s).replaceAll("&amp;")
            );        
        }
     }
}

// Output:
// http://www.example.com/?a=1&amp;b=2&amp;c=3/
// Three in a row: &amp;&amp;&amp;
// &lt; is <, &gt; is >, &apos; is ', etc.
2 голосов
/ 11 июля 2011

Начните с понимания грамматики вокруг сущностей: http://www.w3.org/TR/xml/#NT-EntityRef

Затем посмотрите на JavaDoc для FilterInputStream: http://download.oracle.com/javase/6/docs/api/java/io/FilterInputStream.html

Затем реализуйте ту, которая читает фактический вводимый символ-персонаж.Когда он видит амперсанд, он переключается в «режим объекта» и ищет действительную ссылку на объект (& Name ;).Если он находит один перед первым символом, который не разрешен в Name, он записывает его в выходной дословно.В противном случае записывается &amp;, за которым следует все после амперсанда.

1 голос
/ 11 июля 2011

Вместо того, чтобы пытаться сделать что-то общее для всех возможных плохих данных, просто устраняйте случаи плохих данных, по одному за раз.Скорее всего, все, что генерирует XML, портит один или два символа, но не все.Конечно, это предположение.

Попробуйте просто заменить все & на & EXCEPT, когда за & следует amp ;.Если следующий неправильно кодированный символ, с которым вы столкнетесь, будет <, то замените их все на <.Сохраняйте набор правил небольшим и управляемым, имея дело только с тем, что, как вы знаете, неправильно. </p>

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

Я просто хочу также отметить, что лучшее решение состоит в том, чтобы побудить любого, кто производит XML, исправить кодировку на своей стороне.Это может быть неудобно, но если вы объясните им профессионально, что они не генерируют действительный XML, они могут захотеть исправить ошибку (и).Это дало бы дополнительное преимущество следующему человеку, который должен был потреблять его, и ему не нужно было делать какой-то сумасшедший пользовательский код, чтобы обойти проблему, которая должна быть решена в источнике.Учти это хотя бы.Хуже всего может быть то, что вы спрашиваете, они говорят «нет», и вы находитесь там, где вы сейчас находитесь.

0 голосов
/ 04 апреля 2014

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

  1. Определение допустимых ссылок на сущности и ' скрытие ' их от регулярного выражения
  2. замена неэкранированные символы с помощью регулярных выражений
  3. Восстановление ранее ' скрытых ' ссылок на сущности

Скрытие осуществляется путем включения сущностей в пользовательские последовательности символов.например, "#||<ENTITY_NAME>||#"

Для иллюстрации, скажем, у нас есть этот фрагмент XML с неэкранированным символом &:

<NAME>Testname</NAME>
<VALUE>
    random words one &amp; two
    I am sad&happy; at the same time!
    its still &lt; ecstatic
    It is two & three words
    Short form is 2&three
    Now for some invalid entity refs: &amp, &gt, and &lt too.
</VALUE>

Step1: Мы используем регулярное выражение заменить "[&]\(amp|apos|gt|lt|quot\)[;]" на "#||$1||#".Это связано с тем, что действительными ссылками на сущность XML согласно W3C являются amp, lt, gt, apos & quot .Строка теперь выглядит следующим образом:

<NAME>Testname</NAME>
<VALUE>
    random words one #||amp||# two
    I am sad&happy; at the same time!
    its still #||lt||# ecstatic
    It is two & three words
    Short form is 2&three
    Now for some invalid entity refs: &amp, &gt, and &lt too.
</VALUE>

Только действительные ссылки на сущности были скрыты .&happy; остался нетронутым.

Step2: Замените ли регулярное выражение "[&]" на "&amp;".Строка теперь выглядит так:

<NAME>Testname</NAME>
<VALUE>
    random words one #||amp||# two
    I am sad&amp;happy; at the same time!
    its still #||lt||# ecstatic
    It is two &amp; three words
    Short form is 2&amp;three
    Now for some invalid entity refs: &amp;amp, &amp;gt, and &amp;lt too.
</VALUE>

Шаг 3: Замените ли регулярное выражение "#\|\|([a-z]+)\|\|#" на "&$1;".Окончательная исправленная строка теперь выглядит следующим образом:

<NAME>Testname</NAME>
<VALUE>
    random words one &amp; two
    I am sad&amp;happy; at the same time!
    its still &lt; ecstatic
    It is two &amp; three words
    Short form is 2&amp;three
    Now for some invalid entity refs: &amp;amp, &amp;gt, and &amp;lt too.
</VALUE>

Недостатки: Необходимо тщательно выбирать пользовательскую последовательность символов для скрытия действительного объекта, чтобы убедиться, что никакое действительное содержимое не будет случайно содержать такую ​​же последовательность.Хотя шансы минимальны, но признаются, что это не полностью защищенное решение ...

...