Лучший способ кодировать текстовые данные для XML в Java?

Очень похоже на этот вопрос, за исключением Java.

Каков рекомендуемый способ кодирования строк для вывода XML в Java. Строки могут содержать такие символы, как «&», «<» и т. д.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
99
0
189 018
22
Перейти к ответу Данный вопрос помечен как решенный

Ответы 22

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

Очень просто: используйте библиотеку XML. Таким образом, на самом деле это будет верно, вместо того, чтобы требовать подробного знания битов спецификации XML.

Вы можете порекомендовать такую ​​библиотеку? (Я нахожу удивительным, что это не стандартная часть Java версии 5 ... такая обычная задача).

Tim Cooper 16.11.2009 09:23

XML является является частью стандартной среды Java - смотрите org.w3c.sax и org.w3c.dom. Однако есть и более простые в использовании фреймворки, такие как JDom. Обратите внимание, что может не быть метода «кодирования строк для вывода XML» - я больше рекомендовал, чтобы вся задача XML выполнялась с библиотекой, а не просто выполняла биты одновременно с манипуляциями со строками.

Jon Skeet 16.11.2009 09:28

Это не такой уж полезный совет при выводе XHTML - FlyingSaucer требует XML, но я не могу создавать шаблоны через XML-библиотеку :). К счастью, StringTemplate позволяет мне быстро избегать всех объектов String.

Stephen 13.01.2010 13:45

@Stephen: Я бы ожидал, что библиотека XHTML будет использовать библиотеку XML, чтобы все было в здравом уме, но предоставляло API, ориентированный на XHTML. Необходимость экранирования вручную (и убедитесь, что вы все поняли правильно, везде) - не лучшая идея, IMO.

Jon Skeet 13.01.2010 14:19

Чтобы преобразовать дерево DOM в XML-строку, используйте преобразователь без таблицы стилей.

Thorbjørn Ravn Andersen 19.05.2010 11:55

Я бы не назвал это «очень просто». На некоторых платформах нет библиотеки генерации xml, но вам может потребоваться кодировать некоторый текст в xml. Добавить несколько сотен килобайт библиотеки только из-за этой задачи непросто и нежелательно. Я не приму этот ответ.

Pointer Null 05.04.2012 23:50

@mice: вопрос помечен как Java, а в Java есть лоты библиотек XML. Действительно, есть XML API, встроенные в Java, поэтому не было бы необходимости добавлять что-нибудь еще ... но даже если бы вы это сделали, несколько сотен тысяч килобайт редко являются проблемой за пределами мобильных устройств в наши дни. Даже если бы это была не Java, я бы очень осторожно подходил к разработке на платформе, на которой не было никаких XML API ...

Jon Skeet 05.04.2012 23:52

Рассматриваю Android. Он использует Java, и приложения должны быть небольшими. У него есть парсеры xml, но я не знаю об обратном (он называется «сериализатор xml»?).

Pointer Null 05.04.2012 23:55

@mice: DOM API прекрасно способен генерировать XML. Или есть довольно небольшие сторонние библиотеки. (Например, размер jar-файла JDom составляет 114 КБ.) Использование XML API по-прежнему является рекомендуемым способом создания XML.

Jon Skeet 06.04.2012 00:03

Как насчет этого: stackoverflow.com/questions/439298/… для простой цели экранирования текста xml (не построения xml).

Pointer Null 06.04.2012 00:17

@mice: Думаю, я довольно четко изложил свою позицию. Если я хочу что-то делать с XML, я использую XML API. Вот в чем они хороши. По моему опыту, довольно редко нужно избегать XML, если вы не строительство XML. Я не буду комментировать пригодность кода, который я бы в принципе не использовал. (Обновлено: На самом деле, я буду в этом случае. Я прокомментирую напрямую.)

Jon Skeet 06.04.2012 00:26

Нет проблем с вашим подходом. Однако я использую такой код, который создает xml с помощью String.printf и заполняет некоторый текст в предварительно созданной строке xml. Вы можете использовать xml lib, я не могу в моем конкретном случае.

Pointer Null 06.04.2012 00:31

@mice: Похоже, вы для начала выбрали плохой инструмент. Любая библиотека, которая создает для меня XML и вставляет в него фрагменты текста, должна сама выполнять экранирование. Непросто получить полное представление о ваших конкретных требованиях в комментариях, но я, безусловно, поддерживаю свой ответ.

Jon Skeet 06.04.2012 00:35

Просто общее замечание о том, что слово «правильный» подчеркнуто: простое использование любой XML-библиотеки не гарантирует, что это будет правильно ;-). Разработчики библиотеки тоже люди. Конечно, вы будете в безопасности со стандартными вещами или чем-то вроде Apache Commons Lang ... просто всегда удивляйтесь тому, как люди просто слепо доверяют чужому коду ...

Hannes de Jager 10.09.2012 11:45

@JonSkeet Я конвертирую файл csv в xml, просто используя Java (без Groovy). Какие существуют библиотеки XML для такого преобразования? Спасибо!

Charu Khurana 21.04.2014 22:20

@ Ученик: Их много. Возможно, вы захотите начать с jdom.

Jon Skeet 21.04.2014 22:22

Мы используем библиотеку woodstox stax, и у нее нет способа написания текста, в котором он будет кодировать специальные символы. У него есть вызов writeCharacters, но он не кодирует> (кодирует <).

David Thielen 10.06.2018 23:45

@DavidThielen Это не нужно кодировать. Это часто бывает, но спецификация XML IIRC называет это нормальным, чтобы не кодировать.

Jon Skeet 11.06.2018 05:43

Будь осторожен! XML и, следовательно, DOM API поддерживает только символы в диапазонах # x9 | #xA | #xD | [# x20- # xD7FF] | [# xE000- # xFFFD] | [# x10000- # x10FFFF]. Если вы хотите использовать символы за пределами этого диапазона, вы должны дополнительно экранировать их с помощью собственной системы экранирования. Вы можете использовать мои методы здесь stackoverflow.com/a/59475093/3882565.

stonar96 25.12.2019 15:51

@ stonar96 Да, я предполагал, что правильный XML-документ - это желаемый результат. Если вам нужно выразить то, что нельзя выразить в XML, это большая проблема. Жаль, что XML 1.1 так и не стал популярным, поскольку это решает эту проблему.

Jon Skeet 26.12.2019 12:08

Извините, я проголосовал против и ошибся. Теперь не могу вернуть его обратно. Это правильный ответ

Alexandr 27.03.2020 20:02

@Alexandr: На самом деле не проблема :)

Jon Skeet 27.03.2020 21:12

Используйте JAXP и забудьте об обработке текста, это будет сделано за вас автоматически.

Ваша ссылка на испанском языке, что не так полезно для большинства из нас. Лучше Вот этот.

Vivit 07.03.2018 12:01

Просто используйте.

<![CDATA[ your text here ]]>

Это позволит использовать любые символы, кроме концовки

]]>

Таким образом, вы можете включать недопустимые символы, такие как & и>. Например.

<element><![CDATA[ characters such as & and > are allowed ]]></element>

Однако атрибуты необходимо экранировать, поскольку для них нельзя использовать блоки CDATA.

В большинстве случаев это не то, что вам следует делать. Слишком много людей злоупотребляют тегами CDATA. Назначение CDATA - сказать процессору, чтобы он не обрабатывал его как XML, а просто передавал его. Если вы пытаетесь создать файл XML, вы должны создавать XML, а не просто передавать байты через какой-то элемент оболочки.

Mads Hansen 16.05.2009 20:05

@Mads, использование CDATA приводит к правильному XML-файлу, так что это так же хорошо, как и "правильным способом". Если вам это не нравится, затем проанализируйте его, измените идентичность и распечатайте.

Thorbjørn Ravn Andersen 19.05.2010 11:56

Если вы помещаете текст в элемент CDATA, вам нужно избежать закрывающего маркера CDATA: "]]>" ... кроме того, что вы не можете этого избежать. Поэтому вместо этого вам нужно разбить свой код на части, где вы помещаете половину данных в один элемент CDATA, а другую половину - в секунду: <! [CDATA [Эти данные содержат закрывающий маркер CDATA: "]]]]> <! [CDATA [> "поэтому его пришлось разделить.]]> ... В конце концов, может быть намного проще вместо этого просто экранировать '<', '>' и '&'. Конечно, многие приложения игнорируют потенциальную проблему с закрывающими маркерами CDATA в данных. Думаю, незнание - это блаженство. :)

Stijn de Witt 14.12.2010 15:39

@StijndeWitt абсолютно прав. CDATA - не панацея от экранирования специальных символов.

dnault 06.12.2014 01:52

Это плохая идея. CDATA не допускает никаких символов вне кодировки XML.

Florian F 20.02.2020 12:51

В XML-файле (Java и DOM Parser) "& lt;" присутствует как текстовое значение узла, но когда для этого узла используется node.getContentType, оно преобразуется в "<". Есть ли способ получить "& lt;" сам вместо "<"?

Rohit Kumar 06.08.2020 09:47

Как уже упоминалось, использование библиотеки XML - самый простой способ. Если вы действительно хотите сбежать, вы можете заглянуть в StringEscapeUtils из библиотеки Apache Commons Lang.

Это может быть подходящим вариантом, если вас не волнует абсолютная правильность, например, если вы собираете прототип.

Chase Seibert 13.01.2009 21:32

Метод escapeXml StringEscapeUtils кажется немного дорогостоящим. Есть ли более эффективный метод, который работает с StringBuffer вместо String?

CKing 13.09.2012 11:00

Используйте StringEscapeUtils.escapeXml(str) из commons-lang. Я использую его в приложении App Engine - работает как шарм. Вот Документ Java для этой функции:

Oleg K 15.02.2011 22:04

Работает ли этот метод как для содержимого, так и для атрибутов XML? Мне кажется, что это не работает с атрибутами. Кажется, это не ускользнет от \t, \n и \r.

Lii 27.09.2017 14:39

@Lii и \t, \n или \r нужно экранировать?

Betlista 16.04.2020 12:47

Обратите внимание, что StringEscapeUtils.escapeXml() не экранирует управляющие символы, которые во многих ситуациях недопустимы в XML.

Chin 26.11.2020 23:13

Обратите внимание, что он был перемещен с commons-lang на commons-text

Gregor 29.01.2021 18:11

Примечание. Ваш вопрос касается побег, а не кодирование. Для экранирования используется <и т. д., Чтобы синтаксический анализатор мог различать «это команда XML» и «это некоторый текст». Кодировка - это то, что вы указываете в заголовке XML (UTF-8, ISO-8859-1 и т. д.).

Прежде всего, как все говорили, используйте библиотеку XML. XML выглядит простым, но кодирование + экранирование - это темное вуду (которое вы заметите, как только встретите умляуты, японский язык и другие странные вещи, такие как «цифры полной ширины» (& # FF11; is 1)). Обеспечение читабельности XML - задача Сизифа.

Я предлагаю никогда не хитрить в кодировании текста и экранировании в XML. Но не позволяйте этому останавливать вас от попыток; просто помните, когда он вас укусит (и будет).

Тем не менее, если вы используете только UTF-8, чтобы сделать вещи более читабельными, вы можете рассмотреть эту стратегию:

  • Если текст действительно содержит '<', '>' или '&', оберните его в <![CDATA[ ... ]]>
  • Если текст не содержит этих трех символов, не деформируйте его.

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

Это хорошо сработало для меня, чтобы предоставить экранированную версию текстовой строки:

public class XMLHelper {

/**
 * Returns the string where all non-ascii and <, &, > are encoded as numeric entities. I.e. "&lt;A &amp; B &gt;"
 * .... (insert result here). The result is safe to include anywhere in a text field in an XML-string. If there was
 * no characters to protect, the original string is returned.
 * 
 * @param originalUnprotectedString
 *            original string which may contain characters either reserved in XML or with different representation
 *            in different encodings (like 8859-1 and UFT-8)
 * @return
 */
public static String protectSpecialCharacters(String originalUnprotectedString) {
    if (originalUnprotectedString == null) {
        return null;
    }
    boolean anyCharactersProtected = false;

    StringBuffer stringBuffer = new StringBuffer();
    for (int i = 0; i < originalUnprotectedString.length(); i++) {
        char ch = originalUnprotectedString.charAt(i);

        boolean controlCharacter = ch < 32;
        boolean unicodeButNotAscii = ch > 126;
        boolean characterWithSpecialMeaningInXML = ch == '<' || ch == '&' || ch == '>';

        if (characterWithSpecialMeaningInXML || unicodeButNotAscii || controlCharacter) {
            stringBuffer.append("&#" + (int) ch + ";");
            anyCharactersProtected = true;
        } else {
            stringBuffer.append(ch);
        }
    }
    if (anyCharactersProtected == false) {
        return originalUnprotectedString;
    }

    return stringBuffer.toString();
}

}

stringBuffer.append ("& #" + (int) ch + ";"); Это не сработает для многобайтовых символов. Я сейчас сталкиваюсь с этим с символом эмодзи, последовательностью UTF8 F0 9F 98 8D.

Kylar 15.12.2011 20:20

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

Подумайте об этом: XML было предназначен для написания людьми.

Используйте библиотеки для генерации XML, когда ваш XML как «объект» лучше моделирует вашу проблему. Например, если подключаемые модули участвуют в процессе построения этого XML.

Обновлено: что касается того, как на самом деле избежать XML в шаблонах, использование CDATA или escapeXml(string) из JSTL - два хороших решения, escapeXml(string) можно использовать следующим образом:

<%@taglib prefix = "fn" uri = "http://java.sun.com/jsp/jstl/functions"%>

<item>${fn:escapeXml(value)}</item>

Чтобы избежать символов XML, самый простой способ - использовать проект Apache Commons Lang, JAR, загружаемый с: http://commons.apache.org/lang/

Это класс: org.apache.commons.lang3.StringEscapeUtils;

У него есть метод с именем «escapeXml», который возвращает строку с соответствующим экранированием.

Обновление: escapeXml устарел - используйте escapeXml10. Ссылка commons.apache.org/proper/commons-lang/javadocs/api-3.3/org/‌…

Daniel 01.08.2017 06:19

Поведение StringEscapeUtils.escapeXml () изменилось с Commons Lang 2.5 на 3.0. Теперь он больше не экранирует символы Unicode больше 0x7f.

Это хорошо, старый метод должен был немного стараться избежать сущностей, которые можно было просто вставить в документ utf8.

Новые средства защиты, которые будут включены в Google Guava 11.0, также кажутся многообещающими: http://code.google.com/p/guava-libraries/issues/detail?id=799

Вот XML-эскейпер Guava: code.google.com/p/guava-libraries/source/browse/guava/src/co‌ m /…. В общем, я обнаружил, что Guava лучше спроектирован, чем Apache Commons.

jhclark 30.01.2012 22:00

Попробуй это:

String xmlEscapeText(String t) {
   StringBuilder sb = new StringBuilder();
   for(int i = 0; i < t.length(); i++){
      char c = t.charAt(i);
      switch(c){
      case '<': sb.append("&lt;"); break;
      case '>': sb.append("&gt;"); break;
      case '\"': sb.append("&quot;"); break;
      case '&': sb.append("&amp;"); break;
      case '\'': sb.append("&apos;"); break;
      default:
         if (c>0x7e) {
            sb.append("&#"+((int)c)+";");
         }else
            sb.append(c);
      }
   }
   return sb.toString();
}

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

Jon Skeet 06.04.2012 00:29

А перебирать строки Unicode немного сложнее. Смотрите здесь: stackoverflow.com/q/1527856/402322

ceving 26.09.2012 20:33

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

Guy Starbuck 12.12.2012 19:48

Не уверен, что это тонкий, но лучше рассмотреть случай, когда t==null.

Myobis 13.12.2013 03:08

Для сравнения: org.apache.commons.lang3.StringEscapeUtils.escapeXml поддерживает только пять основных XML-сущностей (gt, lt, quot, amp, apos). Обратите внимание, что символы Unicode больше 0x7f больше не экранируются. (источник)

Myobis 13.12.2013 03:17

Разве в вашем случае по умолчанию условие if не должно читаться как «if (c <32 || c> 0x7e) {»? В противном случае вы кодируете все символы меньше пробела как сами себя, что является недопустимым содержимым XML, верно?

chaotic3quilibrium 20.12.2013 02:05

@ chaotic3quilibrium: ожидаемые символы <32 - это только новые строки или вкладки, и они не экранируются.

Pointer Null 20.12.2013 12:35

@PointerNull Вы в этом уверены ?! Я читал МНОЖЕСТВО других мест, от которых тоже нужно сбежать. У вас есть официальная ссылка, которую вы можете процитировать, в которой прямо говорится, что от них нельзя избегать? Если так, я был бы очень признателен (и, возможно, это стало бы препятствием для огромного количества предложений по кодированию этого пространства).

chaotic3quilibrium 20.12.2013 19:01

@PointerNull Хорошо, сейчас это старое, но многие непечатаемые символы Unicode, которые сопоставляются с ASCII для совместимости, будут переданы вместе с этой процедурой. Я думаю о таких вещах, как встроенные нулевые значения 0x00, встроенные «начало текста» 0x02, «конец передачи» 0x04 и так далее. Конечно, этого нельзя ожидать от вашей типичной строки Java, но забавно, как такие вещи проскальзывают.

Edwin Buck 13.11.2014 21:31

Это ужасное решение, никто, читающий, им не пользуется. Это преобразует "& amp;" в "& ampamp;" и что "нет. .

user1003916 17.12.2014 19:06

@ user1003916: экранирование XML предназначено для преобразования любого & вхождения в & amp; так вот как это должно работать. Если вы исключаете уже экранированную строку, это ваша вина.

Pointer Null 19.12.2014 12:33

Я доволен финальной версией. Java SE компактен, быстр и эффективен. В моей книге всегда лучше делать то, что нужно, чем загружать еще 100 МБ вредоносного ПО.

Roger F. Gay 10.11.2015 19:40

Все символы ниже 0x20, кроме 0x09, 0x0A и 0x0D, недопустимы в XML. Это применимо независимо от того, сбежали они или нет. Единственный правильный способ справиться с ними - пропустить их или выбросить исключение. В остальном это хорошее решение, подобное тому, которое мы обычно используем.

Mike B 18.11.2016 13:40

@ceving Здесь вам не нужно иметь дело с Unicode, поскольку все символы вне BMP можно просто скопировать в том виде, в каком они есть. Единственные пять кодовых точек, нуждающихся в обработке, находятся в BMP.

maaartinus 12.10.2019 08:16

Для метода, который также поддерживает недопустимые символы XML, такие как '\u0000', см. Мой ответ здесь stackoverflow.com/a/59475093/3882565.

stonar96 25.12.2019 15:35

Вопрос был в том, «каков рекомендуемый способ». Написание собственного метода для общей задачи - нет.

Florian F 20.02.2020 12:56

StringEscapeUtils.escapeXml() не экранирует управляющие символы (<0x20). XML 1.1 допускает управляющие символы; XML 1.0 этого не делает. Например, XStream.toXML() успешно сериализует управляющие символы объекта Java в XML, который синтаксический анализатор XML 1.0 отклонит.

Чтобы экранировать управляющие символы с помощью Apache commons-lang, используйте

NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))

Вот простое решение, и оно отлично подходит для кодирования акцентированных символов!

String in = "Hi Lârry & Môe!";

StringBuilder out = new StringBuilder();
for(int i = 0; i < in.length(); i++) {
    char c = in.charAt(i);
    if (c < 31 || c > 126 || "<>\"'\\&".indexOf(c) >= 0) {
        out.append("&#" + (int) c + ";");
    } else {
        out.append(c);
    }
}

System.out.printf("%s%n", out);

Выходы

Hi L&#226;rry &#38; M&#244;e!

Разве «31» в первой строке «if» не должно быть «32»; т.е. меньше, чем пробел? И если "31" должно остаться, то не следует ли его исправить, чтобы читать "if (c <= 31 || ..." (дополнительный знак равенства после знака меньше)?

chaotic3quilibrium 20.12.2013 02:03

Хотя я в принципе согласен с Джоном Скитом, иногда у меня нет возможности использовать внешнюю библиотеку XML. И мне кажется странным, что две функции для экранирования / отмены экранирования простого значения (атрибута или тега, а не полного документа) недоступны в стандартных библиотеках XML, включенных в Java.

В результате и на основе различных ответов, которые я видел здесь и в других местах, вот решение, которое я создал (ничто не работало как простое копирование / вставка):

  public final static String ESCAPE_CHARS = "<>&\"\'";
  public final static List<String> ESCAPE_STRINGS = Collections.unmodifiableList(Arrays.asList(new String[] {
      "&lt;"
    , "&gt;"
    , "&amp;"
    , "&quot;"
    , "&apos;"
  }));

  private static String UNICODE_NULL = "" + ((char)0x00); //null
  private static String UNICODE_LOW =  "" + ((char)0x20); //space
  private static String UNICODE_HIGH = "" + ((char)0x7f);

  //should only be used for the content of an attribute or tag      
  public static String toEscaped(String content) {
    String result = content;
    
    if ((content != null) && (content.length() > 0)) {
      boolean modified = false;
      StringBuilder stringBuilder = new StringBuilder(content.length());
      for (int i = 0, count = content.length(); i < count; ++i) {
        String character = content.substring(i, i + 1);
        int pos = ESCAPE_CHARS.indexOf(character);
        if (pos > -1) {
          stringBuilder.append(ESCAPE_STRINGS.get(pos));
          modified = true;
        }
        else {
          if (    (character.compareTo(UNICODE_LOW) > -1)
               && (character.compareTo(UNICODE_HIGH) < 1)
             ) {
            stringBuilder.append(character);
          }
          else {
            //Per URL reference below, Unicode null character is always restricted from XML
            //URL: https://en.wikipedia.org/wiki/Valid_characters_in_XML
            if (character.compareTo(UNICODE_NULL) != 0) {
              stringBuilder.append("&#" + ((int)character.charAt(0)) + ";");
            }
            modified = true;
          }
        }
      }
      if (modified) {
        result = stringBuilder.toString();
      }
    }
    
    return result;
  }

Вышеупомянутое вмещает несколько разных вещей:

  1. избегает использования логики на основе символов до тех пор, пока это не будет абсолютно необходимо - улучшает совместимость с Unicode
  2. пытается быть как можно более эффективным с учетом вероятности того, что второе условие «если» является наиболее часто используемым путем
  3. - чистая функция; т.е. является потокобезопасным
  4. прекрасно оптимизируется со сборщиком мусора, возвращая содержимое StringBuilder только в том случае, если что-то действительно изменилось - в противном случае возвращается исходная строка

В какой-то момент я напишу инверсию этой функции toUnescaped (). У меня просто нет на это времени сегодня. Когда я это сделаю, я обновлю этот ответ кодом. :)

Выглядит довольно хорошо для меня. Я не хочу добавлять в свой проект еще одну банку только для одного метода. Если вы дадите разрешение, могу я скопировать и вставить ваш код в свой?

RuntimeException 23.01.2014 17:15

@SatishMotwani Конечно, вы можете взять приведенный выше код и делать с ним, как хотите. Насколько я понимаю, любой код, опубликованный на StackOverflow, считается свободным от авторских прав (не рассматривается как произведение в целом). С другой стороны, кому-то было бы чрезвычайно сложно выдвинуть какие-либо претензии по поводу авторских прав и ожидать какого-то результата для себя.

chaotic3quilibrium 23.01.2014 18:46

Спасибо, что разрешили :-) Воспользуюсь.

RuntimeException 29.01.2014 19:41

Вы забыли обрабатывать символы NUL. И, может быть, и другие вещи.

David Balažic 12.11.2018 21:10

@ DavidBalažic Хорошо, объясните, пожалуйста, поподробнее, что я мог пропустить? Пожалуйста, прочтите код более внимательно. Я обработал КАЖДЫЙ ОДИН символ Unicode (из 1111998), включая символ null. Можете ли вы объяснить определение двух значений, UNICODE_LOW и UNICODE_HIGH? Пожалуйста, перечитайте if, в котором используются эти два значения. Обратите внимание, что null (\u0000, который является (int)0) не находится между этими двумя значениями. Прочтите, как он становится правильно "экранированным", как ВСЕ символы Unicode, существующие вне диапазона UNICODE_LOW и UNICODE_HIGH, с помощью техники &#.

chaotic3quilibrium 29.08.2020 19:18

@chaotic3quilibrium NULL недопустим в XML (и некоторых других символах). Неважно, как вы это кодируете. Это незаконно. (также: действительно нет необходимости экранировать символы Unicode, они хорошо поддерживаются в XML, за исключением случаев, когда сам документ XML имеет кодировку, отличную от Unicode)

David Balažic 29.08.2020 21:14

@ DavidBalažic Ах. Tysvm за ваше объяснение. Я нашел справочный документ, который явно определяет, что вы утверждаете о Unicode null. Однако это буквально ЕДИНСТВЕННЫЙ запрещенный символ. Многие категорически не одобряют этого, но NUL является единственным явно безоговорочно ограниченным: en.wikipedia.org/wiki/Valid_characters_in_XML

chaotic3quilibrium 30.08.2020 01:36

@ DavidBalažic Я обновил ответ, чтобы теперь включить ограничение, указанное в ссылке, которой я поделился в своем последнем комментарии. Я добавил комментарий и логику, чтобы правильно обработать этот случай. Опять же, tysvm за точный отзыв.

chaotic3quilibrium 30.08.2020 01:46

Попробуйте закодировать XML с помощью сериализатора Apache XML

//Serialize DOM
OutputFormat format    = new OutputFormat (doc); 
// as a String
StringWriter stringOut = new StringWriter ();    
XMLSerializer serial   = new XMLSerializer (stringOut, 
                                          format);
serial.serialize(doc);
// Display the XML
System.out.println(stringOut.toString());
public String escapeXml(String s) {
    return s.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
}

Объединение вызовов replaceAll в цепочку очень неэффективно, особенно для больших строк. Каждый вызов приводит к созданию нового объекта String, который будет висеть до тех пор, пока не будет собран мусор. Кроме того, каждый вызов требует повторного прохождения строки. Это можно объединить в один ручной цикл со сравнениями с каждым целевым символом на каждой итерации.

daiscog 27.01.2015 17:56

Это должен быть принятый ответ, даже если он неэффективен. Решает проблему в одной строке.

Stimpson Cat 13.02.2018 11:15

И в нем много ошибок. См. этот комментарий выше

David Balažic 12.11.2018 21:08

Чтобы исправить эти ошибки, вы можете дополнительно использовать мой метод здесь stackoverflow.com/a/59475093/3882565. Обратите внимание, что это не замена, но может использоваться дополнительно.

stonar96 25.12.2019 15:43

Для тех, кто ищет наиболее быстрое решение: используйте методы из apache commons-lang:

  • StringEscapeUtils.escapeXml10() для xml 1.0
  • StringEscapeUtils.escapeXml11() для xml 1.1
  • StringEscapeUtils.escapeXml() теперь устарел, но обычно использовался в прошлом

Не забудьте включить зависимость:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.5</version> <!--check current version! -->
</dependency>

Этому вопросу восемь лет, и он все еще не является полностью правильным! Нет, вам не нужно импортировать весь сторонний API для выполнения этой простой задачи. Плохой совет.

Следующий метод:

  • правильно обрабатывать символы вне основной многоязычной плоскости
  • в XML требуются escape-символы
  • экранировать любые символы, отличные от ASCII, что является необязательным, но распространенным
  • заменить символы незаконный в XML 1.0 символом подстановки Unicode. Здесь нет лучшего варианта - их удаление также актуально.

Я попытался оптимизировать для наиболее распространенного случая, но при этом убедился, что вы можете пропустить через него / dev / random и получить действительную строку в XML.

public static String encodeXML(CharSequence s) {
    StringBuilder sb = new StringBuilder();
    int len = s.length();
    for (int i=0;i<len;i++) {
        int c = s.charAt(i);
        if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
            c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff);    // UTF16 decode
        }
        if (c < 0x80) {      // ASCII range: test most common case first
            if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) {
                // Illegal XML character, even encoded. Skip or substitute
                sb.append("&#xfffd;");   // Unicode replacement character
            } else {
                switch(c) {
                  case '&':  sb.append("&amp;"); break;
                  case '>':  sb.append("&gt;"); break;
                  case '<':  sb.append("&lt;"); break;
                  // Uncomment next two if encoding for an XML attribute
//                  case '\''  sb.append("&apos;"); break;
//                  case '\"'  sb.append("&quot;"); break;
                  // Uncomment next three if you prefer, but not required
//                  case '\n'  sb.append("&#10;"); break;
//                  case '\r'  sb.append("&#13;"); break;
//                  case '\t'  sb.append("&#9;"); break;

                  default:   sb.append((char)c);
                }
            }
        } else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) {
            // Illegal XML character, even encoded. Skip or substitute
            sb.append("&#xfffd;");   // Unicode replacement character
        } else {
            sb.append("&#x");
            sb.append(Integer.toHexString(c));
            sb.append(';');
        }
    }
    return sb.toString();
}

Обновлено: для тех, кто продолжает настаивать на глупости написания собственного кода для этого, когда есть совершенно хорошие Java API для работы с XML, вам может быть интересно узнать, что StAX API включен в Oracle Java 8 (я не тестировал другие ) не может правильно кодировать содержимое CDATA: он не экранирует]]> последовательности содержимого. Сторонняя библиотека, даже если она является частью ядра Java, не всегда лучший вариант.

+1 за автономный код. Просто сравнивая ваш код с реализация гуавы, мне интересно, а что насчет '\ t', '\ n', '\ r'? См. Также примечания на документы гуавы

jschnasse 17.09.2018 12:51

Нет необходимости экранировать \ n, \ r и \ t, они действительны, хотя они делают форматирование немного некрасивым. Я изменил код, чтобы показать, как их убрать, если вы этого хотите.

Mike B 18.12.2018 15:14

В CDATA есть способ нет "избежать]]>".

kmkaplan 22.10.2019 09:42

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

Mike B 23.10.2019 14:20

Вместо замены недопустимых символов в XML 1.0 символом подстановки Unicode вы можете использовать здесь мои методы stackoverflow.com/a/59475093/3882565.

stonar96 25.12.2019 15:41

Вы можете использовать Библиотека Enterprise Security API (ESAPI), который предоставляет такие методы, как encodeForXML и encodeForXMLAttribute. Взгляните на документацию интерфейса Кодировщик; он также содержит примеры того, как создать экземпляр DefaultEncoder.

Просто замените

 & with &amp;

И для других персонажей:

> with &gt;
< with &lt;
\" with &quot;
' with &apos;

Если вы ищете библиотеку для работы, попробуйте:

  1. Гуава 26,0 задокументировано здесь

    return XmlEscapers.xmlContentEscaper().escape(text);

    Note: There is also an xmlAttributeEscaper()

  2. Текст 1.4 Apache Commons задокументированный здесь

    StringEscapeUtils.escapeXml11(text)

    Note: There is also an escapeXml10() method

Вот что я нашел после повсеместных поисков решения:

Получите библиотеку Jsoup:

<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.12.1</version>
</dependency>

Потом:

import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Entities
import org.jsoup.parser.Parser

String xml = '''<?xml version = "1.0"?>
<SOAP-ENV:Envelope
   xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
   SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding">

   <SOAP-ENV:Body xmlns:m = "http://www.example.org/quotations">
      <m:GetQuotation>
         <m:QuotationsName> MiscroSoft@G>>gle.com </m:QuotationsName>
      </m:GetQuotation>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>'''



Document doc = Jsoup.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")), "UTF-8", "", Parser.xmlParser())
doc.outputSettings().charset("UTF-8")
doc.outputSettings().escapeMode(Entities.EscapeMode.base)

println doc.toString()

Надеюсь, это кому-то поможет

I have created my wrapper here, hope it will helps a lot, Click here You can modify depends on your requirements

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