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


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

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

20 81

20 ответов:

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

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

просто использовать.

<![CDATA[ your text here ]]>

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

]]>

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

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

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

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

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

}

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

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

StringEscapeUtils.escapeXml() не экранирует управляющие символы (XStream.toXML() С радостью сериализует управляющие символы объекта Java в XML, который будет отклонен синтаксическим анализатором XML 1.0.

чтобы избежать управляющих символов с Apache commons-lang, используйте

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

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

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

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

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

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

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

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

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

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

Примечание: Ваш вопрос о побег, а не кодирование. Экранирование-это использование

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

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

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

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

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

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

хотя я согласен с Джоном скитом в принципе, иногда у меня нет возможности использовать внешнюю библиотеку 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;
  }

выше вмещает несколько различных вещей:

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

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

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

класс такой: org.апаш.палата общин.lang3.StringEscapeUtils;

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

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

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

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

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

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

  • правильно обрабатывать символы вне основной многоязычной плоскости
  • escape-символы, необходимые в 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;
                  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();
}

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

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

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!

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

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

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

вы могли бы использовать библиотека 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);

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

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

    StringEscapeUtils.escapeXml11(text)

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