首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么JSON::XS不能生成有效的UTF-8?

为什么JSON::XS不能生成有效的UTF-8?
EN

Stack Overflow用户
提问于 2014-12-07 03:56:29
回答 2查看 404关注 0票数 6

我得到了一些损坏的JSON,我已经将其简化为这个测试用例。

代码语言:javascript
复制
use utf8;
use 5.18.0;
use Test::More;
use Test::utf8;
use JSON::XS;

BEGIN {
    # damn it
    my $builder = Test::Builder->new;
    foreach (qw/output failure_output todo_output/) {
        binmode $builder->$_, ':encoding(UTF-8)';
    }
}

foreach my $string ( 'Deliver «French Bread»', '日本国' ) {
    my $hashref = { value => $string };
    is_sane_utf8 $string, "String: $string";
    my $json = encode_json($hashref);
    is_sane_utf8 $json, "JSON: $json";
    say STDERR $json;
}
diag ord('»');

done_testing;

这是输出:

代码语言:javascript
复制
utf8.t .. 
ok 1 - String: Deliver «French Bread»
not ok 2 - JSON: {"value":"Deliver «French Bread»"}

#   Failed test 'JSON: {"value":"Deliver «French Bread»"}'
#   at utf8.t line 17.
# Found dodgy chars "<c2><ab>" at char 18
# String not flagged as utf8...was it meant to be?
# Probably originally a LEFT-POINTING DOUBLE ANGLE QUOTATION MARK char - codepoint 171 (dec), ab (hex)
{"value":"Deliver «French Bread»"}    
ok 3 - String: 日本国
ok 4 - JSON: {"value":"æ¥æ¬å½"}
1..4
{"value":"日本国"}
# 187

因此,包含guillemet («»)的字符串是有效的UTF-8,但生成的JSON不是。我遗漏了什么?utf8编译指示正确地标记了我的源代码。此外,尾随的187是来自diag的。这还不到255,所以它看起来几乎像是Perl中旧的Unicode bug的变体。(并且测试输出看起来仍然是垃圾。使用Test::Builder)永远不可能做到这一点。

切换到JSON::PP会产生相同的输出。

这是在OS X Yosemite上运行的Perl 5.18.1。

EN

回答 2

Stack Overflow用户

发布于 2014-12-07 04:36:47

is_sane_utf8并不像你想的那样。您应该将已解码的字符串传递给它。我不确定它的意义,但它不是正确的工具。如果要检查字符串是否为有效的UTF-8,可以使用

代码语言:javascript
复制
ok(eval { decode_utf8($string, Encode::FB_CROAK | Encode::LEAVE_SRC); 1 },
   '$string is valid UTF-8');

为了证明JSON::XS是正确的,让我们看一下标记为is_sane_utf8的序列。

代码语言:javascript
复制
          +--------------------- Start of two byte sequence
          |    +---------------- Not zero (good)     
          |    |     +---------- Continuation byte indicator (good)
          |    |     |
          v    v     v
C2 AB = [110]00010 [10]101011

             00010     101011 = 000 1010 1011 = U+00AB = «

下面显示了JSON::XS产生与Encode.pm相同的输出:

代码语言:javascript
复制
use utf8;
use 5.18.0;
use JSON::XS;
use Encode;

foreach my $string ('Deliver «French Bread»', '日本国') {
    my $hashref = { value => $string };
    say(sprintf("Input: U+%v04X", $string));
    say(sprintf("UTF-8 of input: %v02X", encode_utf8($string)));

    my $json = encode_json($hashref);
    say(sprintf("JSON: %v02X", $json));
    say("");
}

输出(添加了一些空格):

代码语言:javascript
复制
Input: U+0044.0065.006C.0069.0076.0065.0072.0020.00AB.0046.0072.0065.006E.0063.0068.0020.0042.0072.0065.0061.0064.00BB
UTF-8 of input:                     44.65.6C.69.76.65.72.20.C2.AB.46.72.65.6E.63.68.20.42.72.65.61.64.C2.BB
JSON: 7B.22.76.61.6C.75.65.22.3A.22.44.65.6C.69.76.65.72.20.C2.AB.46.72.65.6E.63.68.20.42.72.65.61.64.C2.BB.22.7D

Input: U+65E5.672C.56FD
UTF-8 of input:                     E6.97.A5.E6.9C.AC.E5.9B.BD
JSON: 7B.22.76.61.6C.75.65.22.3A.22.E6.97.A5.E6.9C.AC.E5.9B.BD.22.7D
票数 13
EN

Stack Overflow用户

发布于 2014-12-07 06:33:50

JSON::XS生成了有效的UTF-8,但是您在两个不同的上下文中使用了生成的UTF-8编码的字节字符串,这两个上下文都需要字符串。

问题1: Test::utf8

以下是is_sane_utf8失败的两种主要情况:

  1. 您有一个错误编码的字符串,该字符串是从UTF-8字节字符串或双重编码的UTF-8、解码而来的。该字符串完全没有问题,看起来可能是一个“不可靠”的错误编码(使用其文档中的术语)。
  2. 您有一个有效的UTF-8字节字符串,其中包含从U+0080到U+00FF的编码代码点,例如«French Bread».

is_sane_utf8测试仅适用于字符串,并且有可能导致漏报。

问题2:输出编码

所有非JSON字符串都是字符串,而JSON字符串是从JSON编码器返回的UTF-8编码的字节字符串。由于您使用UTF 8层进行TAP输出,因此将字符串隐式编码为:encoding(UTF-8) -8格式,并获得良好的结果,同时对包含PerlIO的字节字符串进行双重编码。然而,STDERR没有设置JSON PerlIO层,所以编码的:encoding字节字符串在warn in中看起来很好,因为它们已经编码并被直接传递出去。

只对带有字符串的IO使用:encoding(UTF-8) PerlIO层,而不是JSON编码器默认返回的UTF8编码的字节字符串。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27335820

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档