Как я могу преобразовать строку Java в сущности xml для версий Unicode выше 3.0?

1

Чтобы преобразовать java-символы в объекты xml, я могу сделать следующее для каждого символа в String:

buf.append("&#x"+ Integer.toHexString(c | 0x10000).substring(1) +";");

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

Если я использую UTF-8 Reader для чтения в String, то предположительно, что String содержит символы в формате, который работает через Unicode 6.0 (потому что Java 7 поддерживает Unicode 6.0 в соответствии с javadoc).

Как только у меня есть эта строка, как я могу написать ее как объекты xml? В идеале я бы использовал api, который продолжил бы работу с выходом новых версий unicode.

  • 0
    Будьте осторожны: строки Java могут содержать любой символ Unicode; Некоторые из них являются незаконными в XML, даже в качестве сущностей.
Теги:
unicode
unicode-string
xml-entities

2 ответа

4
Лучший ответ

Либо вы не используете правильную терминологию, либо здесь существует большая путаница.

Обозначение ссылки символа &#x просто указывает числовой код; он не зависит от версии Unicode, используемой любым читателем или парсером.

Ваш код на самом деле совместим только с Unicode 1.x, потому что он предполагает, что число символов меньше 2 16. Что касается Unicode 2.0, это неверное предположение. Некоторые символы представляются одним Java char, в то время как другие символы представлены два Java char (известного как суррогаты).

Я не уверен, что такое "UTF-8 Reader". Читатель просто считывает значения char и не знает о UTF-8 или любой другой кодировке, кроме InputStreamReader, которая использует CharsetDecoder для перевода байтов в символы с использованием кодировки UTF-8 (или любого другого, что кодирует конкретный CharsetDecoder).

В любом случае, ни один читатель не будет разбирать XML &#x ссылочный символ обозначения. Для этого вы должны использовать синтаксический анализатор XML.

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

Наконец, чтобы записать строку в виде XML, вы можете использовать Formatter:

static String toXML(String s) {
    Formatter formatter = new Formatter();
    int len = s.length();
    for (int i = 0; i < len; i = s.offsetByCodePoints(i, 1)) {
        int c = s.codePointAt(i);
        if (c < 32 || c > 126 || c == '&' || c == '<' || c == '>') {
            formatter.format("&#x%x;", c);
        } else {
            formatter.format("%c", c);
        }
    }
    return formatter.toString();
}

Как вы можете видеть, нет кода, который зависит от версии Unicode, потому что символы являются просто числовыми значениями. Независимо от того, является ли каждое числовое значение присвоенным кодовым номером Unicode, не имеет значения.

(Моим первым наклонением было использование класса XMLStreamWriter, но, как оказалось, XMLStreamWriter, использующий кодировку, отличную от Юникода, такую как ISO-8859-1 или US-ASCII, неправильно выводит суррогатные пары как отдельные объекты символов, как Java 1.8.0_05.)

  • 0
    +1 ваше решение лучше моего (просто нужно избегать одинарных и двойных кавычек, чтобы быть строгим)
  • 0
    Работает как шарм! Да, я имел в виду InputStreamReader и не пытался анализировать xml с помощью ридера. Это решение состояло в том, чтобы просто исправить систему, в которой есть некоторые испорченные данные, возникшие в результате различных проблем с парсером XML.
Показать ещё 1 комментарий
2

Первоначально Java поддерживал Unicode 1.0, заставляя тип символа 16 бит длиной, но Unicode 2.0 представил механизм суррогатного символа для поддержки большего количества символов, чем число, разрешенное в 16 бит, поэтому строки Java стали кодироваться UTF-16; это означает, что некоторым персонажам нужно представить два символа Java, их называют высоким суррогатным персонажем и низким суррогатным персонажем.

Чтобы узнать, какие символы в строках представляют собой пары с высоким/низким суррогатом, вы можете использовать утилиты в Character:

Character.isHighSurrogate(myChar); // returns true if myChar is a high surrogate
Character.isLowSurrogate(myChar); // same for low surrogate

Character.isSurrogate(myChar); // just to know if myChar is a surrogate

Как только вы узнаете, какие символы имеют высокий или низкий суррогат, вам необходимо преобразовать каждую пару в код кодировки unicode с помощью этого метода:

int codePoint = Character.toCodePoint(highSurrogate, lowSurrogate);

Так как кусок кода стоит тысячи слов, это примерный метод для замены символа символа xml non us-ascii внутри строки:

public static String replaceToCharEntities(String str) {
    StringBuilder result = new StringBuilder(str.length());

    char surrogate = 0;
    for(char c: str.toCharArray()) {

        // if char is a high surrogate, keep it to match it
        // against the next char (low surrogate)
        if(Character.isHighSurrogate(c)) {
            surrogate = c;
            continue;
        }

        // get codePoint
        int codePoint;
        if(surrogate != 0) {
            codePoint = Character.toCodePoint(surrogate, c);
            surrogate = 0;
        } else {
            codePoint = c;
        }

        // decide wether using just a char or a character reference
        if(codePoint < 0x20 || codePoint > 0x7E || codePoint == '<'
                || codePoint == '>' || codePoint == '&' || codePoint == '"'
                || codePoint == '\'') {
            result.append(String.format("&#x%x;", codePoint));
        } else {
            result.append(c);
        }
    }

    return result.toString();
}

Следующий пример строки является хорошим, с которым можно протестировать, поскольку он содержит символ без ascii, который может быть представлен 16-битным значением, а также char с парами с высоким/низким суррогатом:

String myString = "text with some non-US chars: 'Ñ' and '?'";
  • 0
    Хороший ответ, но я не думаю, что есть техническая спецификация, что такое «неамериканский» персонаж.
  • 0
    @ TomBlodget вы правы, просто я не смог найти лучшего термина для "простого простого символа, который не требуется переводить в ссылку на символ XML"

Ещё вопросы

Сообщество Overcoder
Наверх
Меню