Кодировка символов JSON в javascript отличается от java

Java-код ниже

    JSONObject obj = new JSONObject();
    try{
        obj.put("alert","•é");
        byte[] test = obj.toString().getBytes("UTF-8");
        logger.info("bytes are"+ test);
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    };

создает JSONObject, который экранирует символ маркера, но не латинскую букву e с гравировкой, например ""•é", байтовый код [123, 34, 97, 108, 101, 114, 116, 34, 58, 34, 92, 117, 50, 48, 50, 50, -61, -87, 34, 125]

Как я могу получить такой же точный вывод в Javascript (с точки зрения последовательности байтов)? Я не понимаю, почему JSONObject экранирует только один символ, но не другой. Я не знаю, по какому правилу он следовал.

Кажется, в javascript я могу либо избежать всего, кроме ASCII (например, \ u007f-\ uffff), либо вообще не избегать.

Спасибо!


person user3277841    schedule 10.06.2014    source источник
comment
Какова цель создания byte[]? Это другая проблема, которую показал побег.   -  person user2864740    schedule 11.06.2014
comment
поскольку длина массива байтов используется позже в бэкэнде, поэтому код javascript внешнего интерфейса должен вычислять точную длину конечного массива байтов в коде java.   -  person user3277841    schedule 11.06.2014
comment
Затем серверная часть должна вычислить длину. Фронтенд может догадаться о длине, но отвечает именно бэкэнд и авторитетный источник (и надо понимать, что сама длина не обязательно каноническая, а просто результат текущей операции).   -  person user2864740    schedule 11.06.2014
comment
К сожалению, пользовательский интерфейс не может позволить себе внутренний вызов для этого, он должен предоставлять обратную связь пользователю сразу после ввода символов.   -  person user3277841    schedule 11.06.2014


Ответы (1)


Происходят две разные вещи: кодировка Unicode и экранирование строки JSON.

Согласно 2.5 строк JSON RFC:

.. Все символы Unicode могут быть заключены в кавычки, за исключением символов, которые должны быть экранированы..

Любой символ может быть экранирован. Если символ находится в базовой многоязычной плоскости (от U+0000 до U+FFFF), то он может быть представлен как последовательность из шести символов .. [и символы вне BMP экранируются как суррогатные пары в кодировке UTF-16]

То есть строки JSON "•é" и "\u2022é" эквивалентны. Какие (дополнительные) символы экранировать, полностью зависит от реализации сериализации, и обе формы допустимы.

Именно эта строка JSON (текст Unicode) может быть закодирована при преобразовании в поток байтов. В примере он закодирован в кодировке UTF-8. Затем строка JSON может быть эквивалентна без байтового эквивалента на уровне потока или символьного эквивалента на уровне текста JSON.


Что касается правил для JSONObject, то он экранируется в соответствии с

    c < ' '
|| (c >= '\u0080' && c < '\u00a0')
|| (c >= '\u2000' && c < '\u2100')

Одна из причин, по которой эти символы в диапазоне [\u2000, \u2100] могут быть экранированы, заключается в том, что полученный JSON также является допустимым JavaScript. В статье JSON: подмножество JavaScript, которого нет, обсуждается проблема: проблема заключается в том, что кодовые точки Unicode \u2028 и \u2029 обрабатываются как разделители строк в строковых литералах JavaScript, но не в JSON. (В этом диапазоне есть и другие символы Unicode Separator: поймать их одним махом)

person user2864740    schedule 11.06.2014
comment
Я понимаю, но в этом случае javascript должен точно знать, как будет выглядеть byteArray, чтобы получить правильную длину (та же длина, которую код java будет использовать позже). Таким образом, быть эквивалентным недостаточно. Внешний код js должен экранировать строку json точно так же, как код java. - person user3277841; 11.06.2014
comment
Это не очень хорошая идея (на самом деле, я хочу сказать, что это ужасная идея). В любом случае я обновил ответ, включив в него правила, используемые с JSONObject. Вам нужно будет написать пользовательскую функцию для выполнения аналогичного экранирования (такое экранирование не гарантируется в какой-либо конкретной реализации JSON.stringify), а затем создать функцию для кодирования UTF-8 или UTF-8-encoded-length-угадай результат - длина подсчета байтов может быть сделана просто путем просмотра значений кодовой точки. Вам также нужно будет иметь дело с пробелами между токенами JSON. - person user2864740; 11.06.2014
comment
@user3277841 user3277841 С какой стати Javascript должен знать, какую длину byte[] будет использовать код Java? Разве весь смысл JSON не в том, чтобы иметь красивый, аккуратный формат строки, который можно передавать, и не нужно беспокоиться о мелочных деталях, подобных этому? - person David Conrad; 11.06.2014
comment
потому что пользовательский интерфейс должен проверять длину строки, когда она вводится пользователем в текстовое поле, а длина строки определяется этим кодом Java в исходном сообщении. - person user3277841; 11.06.2014
comment
@user3277841 user3277841 Длина строки, вероятно, должна быть логическими символами (а не закодированной длиной) для целей пользовательского интерфейса. Рассмотрите возможность добавления резерва для серверной части (т.е. большего размера varchar), если это возможно; можно также свернуть \u.... JSON (через регулярное выражение замены) в Java, чтобы уменьшить разницу в расширении. - person user2864740; 12.06.2014