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

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

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

<![CDATA[ your text here ]]>

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

]]>

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

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

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

Ответ 4

Попробуйте следующее:

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();
}

Ответ 5

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

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();
}

}

Ответ 6

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

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

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

Ответ 7

В то время как идеализм говорит, что использует библиотеку 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>

Ответ 8

Поведение 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

Ответ 9

public String escapeXml(String s) {
    return s.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
}

Ответ 10

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

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

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

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

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

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

Ответ 11

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

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

  • правильно обрабатывать символы вне базовой многоязычной плоскости
  • в XML требуются экранирующие символы
  • экранировать любые не-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, не всегда является лучшим вариантом.

Ответ 12

Хотя я согласен с Джоном Скитом в принципе, иногда у меня нет возможности использовать внешнюю библиотеку XML. И я считаю, что две функции для escape/unescape простого значения (атрибут или тег, а не полный документ) недоступны в стандартных 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_LOW =  "" + ((char)0x20); //space
  private static String UNICODE_HIGH = "" + ((char)0x7f);

  //should only use 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 {
            stringBuilder.append("&#" + ((int)character.charAt(0)) + ";");
            modified = true;
          }
        }
      }
      if (modified) {
        result = stringBuilder.toString();
      }
    }

    return result;
  }

Вышеприведенное содержит несколько разных вещей:

  • избегает использования логики на основе char, пока это не будет абсолютно необходимо - улучшает совместимость с юникодом
  • пытается быть настолько эффективным, насколько это возможно, учитывая вероятность того, что второе условие "если", вероятно, является наиболее используемым путем
  • - чистая функция; то есть поточно-безопасный
  • прекрасно оптимизируется с сборщиком мусора, возвращая только содержимое StringBuilder, если что-то действительно изменилось - в противном случае возвращается исходная строка

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

Ответ 13

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

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

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

Ответ 14

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

Класс выглядит следующим образом: org.apache.commons.lang3.StringEscapeUtils;

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

Ответ 15

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

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!

Ответ 16

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

Ответ 17

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

//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());

Ответ 18

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

Ответ 19

Если вы ищете библиотеку, чтобы выполнить работу, попробуйте:

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

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

    Примечание: есть также xmlAttributeEscaper()

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

    StringEscapeUtils.escapeXml11(text)

    Примечание. Существует также метод escapeXml10()

Ответ 20

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

 & with &amp;

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

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