内容

名称

JSON::PP - 与 JSON::XS 兼容的纯 Perl 模块。

概要

use JSON::PP;

# exported functions, they croak on error
# and expect/generate UTF-8

$utf8_encoded_json_text = encode_json $perl_hash_or_arrayref;
$perl_hash_or_arrayref  = decode_json $utf8_encoded_json_text;

# OO-interface

$json = JSON::PP->new->ascii->pretty->allow_nonref;

$pretty_printed_json_text = $json->encode( $perl_scalar );
$perl_scalar = $json->decode( $json_text );

# Note that JSON version 2.0 and above will automatically use
# JSON::XS or JSON::PP, so you should be able to just:

use JSON;

描述

JSON::PP 是一个纯 Perl JSON 解码器/编码器,并且(几乎)与 Marc Lehmann 用 C 编写的速度更快的 JSON::XS 兼容。当您使用 JSON 模块但没有安装 JSON::XS 时,JSON::PP 充当备用模块。

由于 JSON.pm 的这种备用功能,JSON::PP 试图不比 JSON::XS 更友好 JavaScript(即不转义额外的字符,如 U+2028 和 U+2029 等),以便当您使用 JSON.pm 并为了速度或意外安装 JSON::XS 时,不会静默地丢失这种 JavaScript 友好性。如果您需要 JavaScript 友好的 RFC7159 兼容纯 Perl 模块,请尝试 JSON::Tiny,它源自 Mojolicious Web 框架,并且比 JSON::PP 更小更快。

JSON::PP 自 Perl 5.14 起就包含在 Perl 核心之中,主要用于 CPAN 工具链模块解析 META.json。

函数式接口

本节几乎完全摘自 JSON::XS。encode_jsondecode_json 默认导出。

encode_json

$json_text = encode_json $perl_scalar

将给定的 Perl 数据结构转换为 UTF-8 编码的二进制字符串(即字符串仅包含字节)。在错误时抛出异常。

此函数调用在功能上等同于

$json_text = JSON::PP->new->utf8->encode($perl_scalar)

只是速度更快。

decode_json

$perl_scalar = decode_json $json_text

encode_json 的反向操作:期望一个 UTF-8(二进制)字符串,并尝试将其解析为 UTF-8 编码的 JSON 文本,返回结果引用。在错误时抛出异常。

此函数调用在功能上等同于

$perl_scalar = JSON::PP->new->utf8->decode($json_text)

只是速度更快。

JSON::PP::is_bool

$is_boolean = JSON::PP::is_bool($scalar)

如果传递的标量表示 JSON::PP::true 或 JSON::PP::false,则返回 true,这两个常量分别充当 10,并且也用于在 Perl 字符串中表示 JSON truefalse

在 perl 5.36 及更高版本中,当给出 perl 的标准布尔值(例如比较结果)时,也会返回 true。

有关 JSON 值如何映射到 Perl 的更多信息,请参见下面的 映射

面向对象接口

本节也摘自 JSON::XS。

面向对象的接口允许您在支持的格式范围内配置自己的编码或解码样式。

new

$json = JSON::PP->new

创建一个新的 JSON::PP 对象,可用于对 JSON 字符串进行编码/解码。下面描述的所有布尔标志默认情况下都禁用allow_nonref 除外,它从版本4.0开始默认启用)。

标志的变异器都会再次返回 JSON::PP 对象,因此可以将调用链接起来。

my $json = JSON::PP->new->utf8->space_after->encode({a => [1,2]})
=> {"a": [1, 2]}

ascii

$json = $json->ascii([$enable])

$enabled = $json->get_ascii

如果$enable 为真(或缺失),则encode 方法将不会生成代码范围0..127(即 ASCII)之外的字符。该范围之外的任何 Unicode 字符将使用单个 \uXXXX(BMP 字符)或双重 \uHHHH\uLLLLL 转义序列进行转义,符合 RFC4627。生成的编码 JSON 文本可以被视为本机 Unicode 字符串、ASCII 编码字符串、latin1 编码字符串或 UTF-8 编码字符串,或者 ASCII 的任何其他超集。

如果$enable 为假,则encode 方法将不会转义 Unicode 字符,除非 JSON 语法或其他标志要求这样做。这将导致更快的格式和更紧凑的格式。

另请参阅本文档后面的编码/代码集标志说明部分。

此标志的主要用途是生成可以通过 7 位通道传输的 JSON 文本,因为编码后的 JSON 文本将不包含任何 8 位字符。

JSON::PP->new->ascii(1)->encode([chr 0x10401])
=> ["\ud801\udc01"]

latin1

$json = $json->latin1([$enable])

$enabled = $json->get_latin1

如果$enable 为真(或缺失),则encode 方法将以 latin1(或 iso-8859-1)编码生成的 JSON 文本,转义代码范围0..255之外的任何字符。生成的字符串可以被视为 latin1 编码的 JSON 文本或本机 Unicode 字符串。decode 方法不会受到此标志的任何影响,因为decode 默认情况下期望 Unicode,它是 latin1 的严格超集。

如果$enable 为假,则encode 方法将不会转义 Unicode 字符,除非 JSON 语法或其他标志要求这样做。

另请参阅本文档后面的编码/代码集标志说明部分。

此标志的主要用途是将二进制数据有效地编码为 JSON 文本,因为大多数八位字节不会被转义,从而导致编码后的尺寸更小。缺点是生成的 JSON 文本以 latin1 编码(并且在存储和传输时必须正确地以这种方式处理),这对于 JSON 来说是一种罕见的编码。因此,当您想要将已知包含二进制数据的结构有效地存储在文件或数据库中时,它最有用,而不是与其他 JSON 编码器/解码器进行通信时。

JSON::PP->new->latin1->encode (["\x{89}\x{abc}"]
=> ["\x{89}\\u0abc"]    # (perl syntax, U+abc escaped, U+89 not)

utf8

$json = $json->utf8([$enable])

$enabled = $json->get_utf8

如果 $enable 为真(或缺失),则 encode 方法将把 JSON 结果编码为 UTF-8,这是许多协议所要求的,而 decode 方法则期望处理一个 UTF-8 编码的字符串。请注意,UTF-8 编码的字符串不包含任何超出范围 0..255 的字符,因此它们对于字节级/二进制 I/O 很有用。在将来的版本中,启用此选项可能会启用对 UTF-16 和 UTF-32 编码系列的自动检测,如 RFC4627 中所述。

如果 $enable 为假,则 encode 方法将返回 JSON 字符串作为(未编码的)Unicode 字符串,而 decode 则期望一个 Unicode 字符串。任何解码或编码(例如,到 UTF-8 或 UTF-16)都需要您自己完成,例如使用 Encode 模块。

另请参阅本文档后面的编码/代码集标志说明部分。

示例,输出 UTF-16BE 编码的 JSON

use Encode;
$jsontext = encode "UTF-16BE", JSON::PP->new->encode ($object);

示例,解码 UTF-32LE 编码的 JSON

use Encode;
$object = JSON::PP->new->decode (decode "UTF-32LE", $jsontext);

pretty

$json = $json->pretty([$enable])

这在一个调用中启用(或禁用)所有 indentspace_beforespace_after(以及将来可能更多的)标志,以生成最易读(或最紧凑)的格式。

indent

$json = $json->indent([$enable])

$enabled = $json->get_indent

如果 $enable 为真(或缺失),则 encode 方法将使用多行格式作为输出,将每个数组成员或对象/哈希键值对放在自己的行中,并正确缩进。

如果 $enable 为假,则不会生成任何换行符或缩进,并且生成的 JSON 文本保证不包含任何 换行符

此设置对解码 JSON 文本没有影响。

默认缩进空格长度为 3。您可以使用 indent_length 更改长度。

space_before

$json = $json->space_before([$enable])

$enabled = $json->get_space_before

如果 $enable 为真(或缺失),则 encode 方法将在 JSON 对象中分隔键和值的 : 之前添加一个额外的可选空格。

如果 $enable 为假,则 encode 方法不会在这些地方添加任何额外的空格。

此设置对解码 JSON 文本没有影响。您也最有可能将此设置与 space_after 结合使用。

示例,space_before 启用,space_after 和 indent 禁用

{"key" :"value"}

space_after

$json = $json->space_after([$enable])

$enabled = $json->get_space_after

如果 $enable 为真(或缺失),则 encode 方法将在 JSON 对象中分隔键和值的 : 之后添加一个额外的可选空格,以及在分隔键值对和数组成员的 , 之后添加额外的空格。

如果 $enable 为假,则 encode 方法不会在这些地方添加任何额外的空格。

此设置对解码 JSON 文本没有影响。

示例,space_before 和 indent 禁用,space_after 启用

{"key": "value"}

relaxed

$json = $json->relaxed([$enable])

$enabled = $json->get_relaxed

如果 $enable 为真(或缺失),则 decode 将接受对正常 JSON 语法的某些扩展(见下文)。encode 不会受到任何影响。请注意,此选项使您接受无效的 JSON 文本,就好像它们是有效的!。我建议只使用此选项来解析由人类编写的特定于应用程序的文件(配置文件、资源文件等)。

如果 $enable 为假(默认值),则 decode 将只接受有效的 JSON 文本。

目前接受的扩展是

规范

$json = $json->canonical([$enable])

$enabled = $json->get_canonical

如果 $enable 为真(或缺失),则 encode 方法将通过对键进行排序来输出 JSON 对象。这会增加相对较高的开销。

如果 $enable 为假,则 encode 方法将按 Perl 存储键值对的顺序输出键值对(这很可能在同一脚本的不同运行之间发生变化,并且从 5.18 开始甚至可能在同一运行中发生变化)。

如果您希望将相同的数据结构编码为相同的 JSON 文本(在相同的整体设置下),此选项很有用。如果禁用它,即使包含相同的数据,相同的哈希也可能被编码为不同的方式,因为键值对在 Perl 中没有固有的排序。

此设置对解码 JSON 文本没有影响。

此设置目前对绑定哈希没有影响。

allow_nonref

$json = $json->allow_nonref([$enable])

$enabled = $json->get_allow_nonref

与其他布尔选项不同,此选项从版本 4.0 开始默认启用。

如果 $enable 为真(或缺失),则 encode 方法可以将非引用转换为其对应的字符串、数字或空 JSON 值,这是对 RFC4627 的扩展。同样,decode 将接受这些 JSON 值而不是报错。

如果 $enable 为假,则 encode 方法如果未传递数组引用或哈希引用,将报错,因为 JSON 文本必须是对象或数组。同样,decode 如果给定不是 JSON 对象或数组的内容,将报错。

示例,在未启用 allow_nonref 的情况下将 Perl 标量编码为 JSON 值,导致错误

JSON::PP->new->allow_nonref(0)->encode ("Hello, World!")
=> hash- or arrayref expected...

allow_unknown

$json = $json->allow_unknown([$enable])

$enabled = $json->get_allow_unknown

如果 $enable 为真(或缺失),则 encode 在遇到无法用 JSON 表示的值(例如文件句柄)时,不会抛出异常,而是会编码一个 JSON null 值。请注意,blessed 对象不包括在此处,并由 c<allow_blessed> 单独处理。

如果 $enable 为假(默认值),则 encode 在遇到无法编码为 JSON 的任何内容时,都会抛出异常。

此选项不会以任何方式影响 decode,建议除非您了解通信伙伴,否则不要将其关闭。

allow_blessed

$json = $json->allow_blessed([$enable])

$enabled = $json->get_allow_blessed

有关详细信息,请参见 "对象序列化"

如果 $enable 为真(或缺失),则 encode 方法在遇到无法转换的 blessed 引用时,不会抛出异常。相反,会编码一个 JSON null 值,而不是对象。

如果 $enable 为假(默认值),则 encode 在遇到无法转换的 blessed 对象时,会抛出异常。

此设置对 decode 没有影响。

convert_blessed

$json = $json->convert_blessed([$enable])

$enabled = $json->get_convert_blessed

有关详细信息,请参见 "对象序列化"

如果 $enable 为真(或缺失),则 encode 在遇到 blessed 对象时,会检查对象类上是否存在 TO_JSON 方法。如果找到,它将在标量上下文中被调用,并且生成的标量将被编码,而不是对象。

TO_JSON 方法可以安全地调用 die,如果需要。如果 TO_JSON 返回其他 blessed 对象,则将以相同的方式处理这些对象。TO_JSON 必须注意不要在这种情况下导致无限递归循环(== 崩溃)。选择 TO_JSON 的名称是因为 Perl 核心调用的其他方法(== 不是由对象的用户调用)通常使用大写字母,并且为了避免与任何 to_json 函数或方法发生冲突。

如果 $enable 为假(默认值),则 encode 不会考虑这种类型的转换。

此设置对 decode 没有影响。

allow_tags

$json = $json->allow_tags([$enable])

$enabled = $json->get_allow_tags

有关详细信息,请参见 "对象序列化"

如果 $enable 为真(或缺失),那么 encode 在遇到一个 blessed 对象时,会检查该对象的类是否具有 FREEZE 方法。如果找到,它将被用来将对象序列化成一个非标准的带标签的 JSON 值(JSON 解码器无法解码)。

它还会导致 decode 解析这种带标签的 JSON 值,并通过调用 THAW 方法对其进行反序列化。

如果 $enable 为假(默认值),那么 encode 将不会考虑这种类型的转换,带标签的 JSON 值会导致 decode 中出现解析错误,就好像标签不是语法的一部分一样。

boolean_values

$json->boolean_values([$false, $true])

($false,  $true) = $json->get_boolean_values

默认情况下,JSON 布尔值将被解码为重载的 $JSON::PP::false$JSON::PP::true 对象。

使用此方法,您可以指定自己的布尔值进行解码 - 在解码时,JSON false 将被解码为 $false 的副本,JSON true 将被解码为 $true(这里的“副本”与将值分配给另一个变量相同,即 $copy = $false)。

当您想要将解码后的数据结构直接传递给其他序列化器(如 YAML、Data::MessagePack 等)时,这很有用。

请注意,这仅在您 decode 时有效。您可以设置不兼容的布尔对象(如 boolean),但是当您 encode 具有这种布尔对象的结构时,您仍然需要启用 convert_blessed(如果需要,还需要添加 TO_JSON 方法)。

在不带任何参数的情况下调用此方法将重置布尔值到它们的默认值。

get_boolean_values 将返回 $false$true 两个值,或者当它们设置为默认值时返回空列表。

core_bools

$json->core_bools([$enable]);

如果 $enable 为真(或缺失),那么 decode 将生成标准的 Perl 布尔值。等同于调用

$json->boolean_values(!!1, !!0)

get_core_bools 将在设置此值时返回 true。在 Perl 5.36 中,如果使用 boolean_values 方法将布尔值设置为 Perl 的核心布尔值,它也会返回 true。

方法 unblessed_boolget_unblessed_bool 作为别名提供,以与 Cpanel::JSON::XS 保持兼容。

filter_json_object

$json = $json->filter_json_object([$coderef])

当指定 $coderef 时,它将在每次解码 JSON 对象时从 decode 中调用。唯一的参数是新创建的哈希的引用。如果代码引用返回单个标量(它不必是引用),则此值(或更确切地说是它的副本)将插入反序列化数据结构中。如果它返回一个空列表(注意:不是 undef,它是一个有效的标量),则将插入原始反序列化的哈希。此设置会大大降低解码速度。

$coderef 被省略或未定义时,任何现有的回调都将被删除,并且 decode 不会以任何方式更改反序列化的哈希。

示例,将所有 JSON 对象转换为整数 5

my $js = JSON::PP->new->filter_json_object(sub { 5 });
# returns [5]
$js->decode('[{}]');
# returns 5
$js->decode('{"a":1, "b":2}');

filter_json_single_key_object

$json = $json->filter_json_single_key_object($key [=> $coderef])

filter_json_object 的工作方式非常相似,但仅针对具有名为 $key 的单个键的 JSON 对象调用。

$coderef 在通过 filter_json_object 指定的回调(如果有)之前调用。它将传递 JSON 对象中的单个值。如果它返回单个值,它将被插入数据结构中。如果它不返回任何值(甚至不返回 undef,而是返回空列表),则将调用 filter_json_object 的回调,就好像没有指定单键回调一样。

如果 $coderef 被省略或未定义,则相应的回调将被禁用。对于给定的键,只能有一个回调。

由于此回调的调用频率低于 filter_json_object 回调,因此解码速度通常不会受到太大影响。因此,单键对象是将 Perl 对象序列化为 JSON 对象的绝佳目标,尤其是因为单键 JSON 对象与类型标记值概念最为接近(它基本上是一个 ID/VALUE 元组)。当然,JSON 并不支持这种方式,因此您需要确保您的数据永远不会像序列化的 Perl 哈希一样。

单对象键的典型名称是 __class_whatever__$__dollars_are_rarely_used__$}ugly_brace_placement,甚至像 __class_md5sum(classname)__ 这样的东西,以降低与真实哈希冲突的风险。

例如,将格式为 { "__widget__" => <id> } 的 JSON 对象解码为相应的 $WIDGET{<id>} 对象。

# return whatever is in $WIDGET{5}:
JSON::PP
   ->new
   ->filter_json_single_key_object (__widget__ => sub {
         $WIDGET{ $_[0] }
      })
   ->decode ('{"__widget__": 5')

# this can be used with a TO_JSON method in some "widget" class
# for serialisation to json:
sub WidgetBase::TO_JSON {
   my ($self) = @_;

   unless ($self->{id}) {
      $self->{id} = ..get..some..id..;
      $WIDGET{$self->{id}} = $self;
   }

   { __widget__ => $self->{id} }
}

shrink

$json = $json->shrink([$enable])

$enabled = $json->get_shrink

如果 $enable 为真(或缺失),则 encode 返回的字符串将被缩短(即,如果可能,将被降级)。

缩短的实际定义可能会在将来的版本中发生变化,但它始终会尝试以牺牲时间为代价来节省空间。

如果 $enable 为假,则 JSON::PP 不会执行任何操作。

max_depth

$json = $json->max_depth([$maximum_nesting_depth])

$max_depth = $json->get_max_depth

设置编码或解码时接受的最大嵌套级别(默认值为 512)。如果在 JSON 文本或 Perl 数据结构中检测到更高的嵌套级别,则编码器和解码器将在该点停止并发出错误。

嵌套级别由编码器需要遍历以到达给定点的哈希或数组引用数量,或者由 {[ 字符(不包括其匹配的结束括号)交叉以到达字符串中给定字符的数量来定义。

将最大深度设置为 1 将禁止任何嵌套,从而确保对象仅为单个哈希/对象或数组。

如果没有给出参数,将使用最高可能的设置,这很少有用。

有关此功能为何有用的更多信息,请参阅 "JSON::XS 中的安全性注意事项"

max_size

$json = $json->max_size([$maximum_string_size])

$max_size = $json->get_max_size

设置 JSON 文本在尝试解码时可以具有的最大长度(以字节为单位)。默认值为 0,表示没有限制。当对长度超过此字节数的字符串调用 decode 时,它不会尝试解码该字符串,而是会抛出异常。此设置对 encode(目前)没有影响。

如果没有给出参数,则将停用限制检查(与指定 0 时相同)。

有关此功能为何有用的更多信息,请参阅 "JSON::XS 中的安全性注意事项"

encode

$json_text = $json->encode($perl_scalar)

将给定的 Perl 值或数据结构转换为其 JSON 表示形式。在错误时发出错误。

decode

$perl_scalar = $json->decode($json_text)

encode 的反面:期望一个 JSON 文本并尝试解析它,返回生成的简单标量或引用。在错误时发出错误。

decode_prefix

($perl_scalar, $characters) = $json->decode_prefix($json_text)

此方法类似于 decode 方法,但当第一个 JSON 对象之后存在尾部垃圾时,它不会抛出异常,而是会默默地停止解析并返回到目前为止已消耗的字符数。

如果您的 JSON 文本没有由外部协议分隔,并且您需要知道 JSON 文本的结束位置,则此方法很有用。

JSON::PP->new->decode_prefix ("[1] the tail")
=> ([1], 3)

JSON::PP 专用标志

以下标志和属性仅适用于 JSON::PP。如果您使用其中任何一个,则无法通过将 JSON::PP 替换为 JSON::XS 来使您的应用程序运行得更快。如果您需要这些标志并且还需要速度提升,您可能需要尝试使用 Cpanel::JSON::XS,它是 Reini Urban 对 JSON::XS 的一个分支,它支持其中一些标志(但存在不同的不兼容性)。大多数这些历史标志仅出于向后兼容性而保留,不应在新的应用程序中使用。

allow_singlequote

$json = $json->allow_singlequote([$enable])
$enabled = $json->get_allow_singlequote

如果 $enable 为真(或缺失),则 decode 将接受包含以单引号开头和结尾的字符串的无效 JSON 文本。encode 不会受到任何影响。请注意,此选项会让您将无效的 JSON 文本视为有效文本!。我建议仅将此选项用于解析由人类编写的特定于应用程序的文件(配置文件、资源文件等)。

如果 $enable 为假(默认值),则 decode 将只接受有效的 JSON 文本。

$json->allow_singlequote->decode(qq|{"foo":'bar'}|);
$json->allow_singlequote->decode(qq|{'foo':"bar"}|);
$json->allow_singlequote->decode(qq|{'foo':'bar'}|);

allow_barekey

$json = $json->allow_barekey([$enable])
$enabled = $json->get_allow_barekey

如果 $enable 为真(或缺失),则 decode 将接受包含 JSON 对象的无效 JSON 文本,这些对象的名字不以引号开头和结尾。encode 不会受到任何影响。请注意,此选项会让您将无效的 JSON 文本视为有效文本!。我建议仅将此选项用于解析由人类编写的特定于应用程序的文件(配置文件、资源文件等)。

如果 $enable 为假(默认值),则 decode 将只接受有效的 JSON 文本。

$json->allow_barekey->decode(qq|{foo:"bar"}|);

allow_bignum

$json = $json->allow_bignum([$enable])
$enabled = $json->get_allow_bignum

如果 $enable 为真(或缺失),则 decode 将把 Perl 无法处理的大整数转换为 Math::BigInt 对象,并将浮点数转换为 Math::BigFloat 对象。encode 将把 Math::BigIntMath::BigFloat 对象转换为 JSON 数字。

$json->allow_nonref->allow_bignum;
$bigfloat = $json->decode('2.000000000000000000000000001');
print $json->encode($bigfloat);
# => 2.000000000000000000000000001

另请参见 映射

loose

$json = $json->loose([$enable])
$enabled = $json->get_loose

如果 $enable 为真(或缺失),则 decode 将接受包含未转义的 [\x00-\x1f\x22\x5c] 字符的无效 JSON 文本。encode 不会受到任何影响。请注意,此选项会让您将无效的 JSON 文本视为有效文本!。我建议仅将此选项用于解析由人类编写的特定于应用程序的文件(配置文件、资源文件等)。

如果 $enable 为假(默认值),则 decode 将只接受有效的 JSON 文本。

$json->loose->decode(qq|["abc
                               def"]|);

escape_slash

$json = $json->escape_slash([$enable])
$enabled = $json->get_escape_slash

如果 $enable 为真(或缺失),则 encode 将显式地转义斜杠(斜线;U+002F)字符,以降低由 JSON 文本中的 </script> 引起的 XSS(跨站点脚本)风险,但代价是会增加 JSON 文本的大小。

当您将 JSON 嵌入 HTML 时,此选项可能很有用,但通常情况下,将任意 JSON 嵌入 HTML(通过某些 HTML 模板工具或字符串插值)是有风险的。您必须根据上下文以正确的顺序转义必要的字符。

decode 不会受到任何影响。

indent_length

$json = $json->indent_length($number_of_spaces)
$length = $json->get_indent_length

此选项仅在您还启用 indentpretty 时才有用。

JSON::XS 在您 encode 时使用三个空格缩进(如果由 indentpretty 请求),并且该数字无法更改。JSON::PP 允许您使用这些 mutator/accessor 来更改/获取缩进空格的数量。默认空格数为三个(与 JSON::XS 相同),可接受的范围为 0(无缩进;最好通过 indent(0) 禁用缩进)到 15

sort_by

$json = $json->sort_by($code_ref)
$json = $json->sort_by($subroutine_name)

如果您只想在 encode 时对 JSON 对象中的键(名称)进行排序,请启用 canonical 选项(见上文),该选项允许您按字母顺序对对象键进行排序。

如果您出于任何原因需要非字母排序,可以将代码引用(或子例程名称)传递给 sort_by,然后该参数将传递给 Perl 的 sort 内置函数。

由于排序是在 JSON::PP 范围内完成的,因此您通常需要在子例程名称前加上 JSON::PP::,以及 sort 函数使用的子例程中使用的特殊变量 $a$b

示例

my %ORDER = (id => 1, class => 2, name => 3);
$json->sort_by(sub {
    ($ORDER{$JSON::PP::a} // 999) <=> ($ORDER{$JSON::PP::b} // 999)
    or $JSON::PP::a cmp $JSON::PP::b
});
print $json->encode([
    {name => 'CPAN', id => 1, href => 'http://cpan.org'}
]);
# [{"id":1,"name":"CPAN","href":"http://cpan.org"}]

请注意,sort_by 会影响数据结构中所有普通哈希。如果您需要更精细的控制,请使用实现有序哈希的模块(例如 Hash::OrderedTie::IxHash)将必要的哈希绑定。canonicalsort_by 不会影响绑定哈希中的键顺序。

use Hash::Ordered;
tie my %hash, 'Hash::Ordered',
    (name => 'CPAN', id => 1, href => 'http://cpan.org');
print $json->encode([\%hash]);
# [{"name":"CPAN","id":1,"href":"http://cpan.org"}] # order is kept

增量解析

本节也摘自 JSON::XS。

在某些情况下,需要对 JSON 文本进行增量解析。虽然此模块始终需要将 JSON 文本和生成的 Perl 数据结构同时保存在内存中,但它允许您增量解析 JSON 流。它是通过累积文本直到获得完整的 JSON 对象来实现的,然后可以对其进行解码。此过程类似于使用 decode_prefix 来查看是否可用完整的 JSON 对象,但效率更高(并且可以使用最少的函数调用来实现)。

JSON::PP 只有在确定拥有足够文本以获得决定性结果时才会尝试解析 JSON 文本,它使用一个非常简单但真正增量的解析器。这意味着它有时不会像完整解析器那样早地停止,例如,它不会检测到不匹配的括号。它唯一保证的是,一旦看到语法有效的 JSON 文本,它就开始解码。这意味着您需要设置资源限制(例如 max_size)以确保解析器在出现语法错误时停止解析。

以下方法实现了此增量解析器。

incr_parse

$json->incr_parse( [$string] ) # void context

$obj_or_undef = $json->incr_parse( [$string] ) # scalar context

@obj_or_empty = $json->incr_parse( [$string] ) # list context

这是主要的解析函数。它可以追加新文本并从已累积的流中提取对象(这两个功能都是可选的)。

如果给出了 $string,则此字符串将附加到已存储在 $json 对象中的现有 JSON 片段。

之后,如果在 void 上下文中调用该函数,它将简单地返回而不会执行任何进一步的操作。这可用于以您想要的任何块数添加更多文本。

如果在标量上下文中调用该方法,则它将尝试提取恰好 一个 JSON 对象。如果成功,它将返回此对象,否则将返回 undef。如果存在解析错误,此方法将像 decode 一样抛出异常(然后可以使用 incr_skip 跳过错误部分)。这是使用该方法最常见的方式。

最后,在列表上下文中,它将尝试从流中提取尽可能多的对象并返回它们,否则返回空列表。为了使此方法起作用,JSON 对象或数组之间不能有分隔符(空格除外),而是必须将它们串联在一起。如果发生错误,则会像在标量上下文情况下一样引发异常。请注意,在这种情况下,任何先前解析的 JSON 文本都将丢失。

示例:解析给定字符串中的一些 JSON 数组/对象并返回它们。

my @objs = JSON::PP->new->incr_parse ("[5][7][1,2]");

incr_text

$lvalue_string = $json->incr_text

此方法将当前存储的 JSON 片段作为左值返回,也就是说,您可以对其进行操作。这标量上下文中对incr_parse的先前调用成功返回对象时才有效。在所有其他情况下,您都不得调用此函数(我是认真的。虽然在简单的测试中它可能确实有效,但在现实世界条件下它失败)。作为特殊例外,您也可以在解析任何内容之前调用此方法。

这意味着您只能使用此函数在完整 JSON 对象之前或之后查看或操作文本,而不能在解析器正在解析 JSON 对象的过程中使用。

此函数在两种情况下很有用:a) 查找 JSON 对象后的尾随文本,或 b) 解析由非 JSON 文本(例如逗号)分隔的多个 JSON 对象。

incr_skip

$json->incr_skip

这将重置增量解析器的状态,并将到目前为止解析的文本从输入缓冲区中删除。这在incr_parse失败后很有用,在这种情况下,输入缓冲区和增量解析器状态保持不变,以跳过到目前为止解析的文本并重置解析状态。

incr_reset的区别在于,只删除了发生解析错误之前的文本。

incr_reset

$json->incr_reset

这将完全重置增量解析器,也就是说,在此调用之后,它将就像解析器从未解析过任何内容一样。

如果您想重复解析 JSON 对象并忽略任何尾随数据,这很有用,这意味着您必须在每次成功解码后重置解析器。

MAPPING

本节的大部分内容也来自 JSON::XS。

本节描述了 JSON::PP 如何将 Perl 值映射到 JSON 值,反之亦然。这些映射旨在“在大多数情况下自动执行正确的事情”,保留往返特性(您输入的内容将作为等效的内容输出)。

对于更了解的人来说:请注意,在以下描述中,小写 perl 指的是 Perl 解释器,而大写 Perl 指的是抽象的 Perl 语言本身。

JSON -> PERL

对象

JSON 对象在 Perl 中成为对哈希的引用。不会保留对象键的顺序(JSON 本身也不保留对象键的顺序)。

数组

JSON 数组在 Perl 中成为对数组的引用。

字符串

JSON 字符串在 Perl 中成为字符串标量 - JSON 中的 Unicode 代码点在 Perl 字符串中由相同的代码点表示,因此不需要手动解码。

数字

JSON 数字在 perl 中成为整数、数值(浮点数)或字符串标量,具体取决于其范围和任何小数部分。在 Perl 层面上,这些之间没有区别,因为 Perl 处理所有转换细节,但整数可能占用更少的内存,并且可能比浮点数更精确地表示更多值。

如果数字仅包含数字,JSON::PP 将尝试将其表示为整数值。如果失败,它将尝试将其表示为数值(浮点数)值,如果在不损失精度的情况下可以做到这一点。否则,它将保留数字作为字符串值(在这种情况下,您将失去往返能力,因为 JSON 数字将被重新编码为 JSON 字符串)。

包含小数或指数部分的数字将始终表示为数值(浮点数)值,可能会损失精度(在这种情况下,您可能会失去完美的往返能力,但 JSON 数字仍将被重新编码为 JSON 数字)。

请注意,精度不是准确性 - 二进制浮点数不能精确地表示大多数十进制小数,并且在从浮点数转换到浮点数时,JSON::PP 仅保证精度达到但不包括最低有效位。

当启用 allow_bignum 时,大整数和大数将分别转换为 Math::BigIntMath::BigFloat 对象,而不会成为字符串标量或损失精度。

true, false

这些 JSON 原子分别成为 JSON::PP::trueJSON::PP::false。它们被重载以几乎完全像数字 10 一样工作。您可以使用 JSON::PP::is_bool 函数检查标量是否为 JSON 布尔值。

null

JSON null 原子在 Perl 中成为 undef

shell 风格注释 (# text)

作为 JSON 语法的非标准扩展,由 relaxed 设置启用,允许使用 shell 风格注释。它们可以从字符串外部的任何位置开始,一直到行尾。

标记值 ((tag)value)。

另一个 JSON 语法非标准扩展,通过 allow_tags 设置启用,即标记值。在此实现中,tag 必须是作为 JSON 字符串编码的 Perl 包/类名称,而 value 必须是编码可选构造函数参数的 JSON 数组。

有关详细信息,请参阅下面的 "对象序列化"

PERL -> JSON

从 Perl 到 JSON 的映射稍微复杂一些,因为 Perl 是一种真正的无类型语言,因此我们只能猜测 Perl 值所指的 JSON 类型。

哈希引用

Perl 哈希引用变为 JSON 对象。由于哈希键(或 JSON 对象)中没有固有的排序,因此它们通常会以伪随机顺序进行编码。JSON::PP 可以选择对哈希键进行排序(由 canonical 标志和/或 sort_by 属性确定),因此相同的数据结构将序列化为相同的 JSON 文本(给定相同的设置和 JSON::PP 版本),但这会产生运行时开销,并且很少有用,例如,当您想要将一些 JSON 文本与另一个 JSON 文本进行比较以判断是否相等时。

数组引用

Perl 数组引用变为 JSON 数组。

其他引用

其他未祝福的引用通常不允许,并且会导致抛出异常,但对整数 01 的引用除外,它们在 JSON 中将转换为 falsetrue 原子。您也可以使用 JSON::PP::falseJSON::PP::true 来提高可读性。

to_json [\0, JSON::PP::true]      # yields [false,true]
JSON::PP::true,JSON::PP::false

这些特殊值分别变为 JSON true 和 JSON false 值。您也可以直接使用 \1\0

JSON::PP::null

此特殊值变为 JSON null。

祝福对象

祝福对象不能直接在 JSON 中表示,但 JSON::PP 允许各种处理对象的方式。有关详细信息,请参阅下面的 "对象序列化"

简单标量

简单的 Perl 标量(任何不是引用的标量)是最难编码的对象:JSON::PP 将把未定义的标量编码为 JSON null 值,将最后在编码之前以字符串上下文使用的标量编码为 JSON 字符串,并将其他任何内容编码为数字值

# dump as number
encode_json [2]                      # yields [2]
encode_json [-3.0e17]                # yields [-3e+17]
my $value = 5; encode_json [$value]  # yields [5]

# used as string, so dump as string
print $value;
encode_json [$value]                 # yields ["5"]

# undef becomes null
encode_json [undef]                  # yields [null]

您可以通过将其字符串化来强制类型为 JSON 字符串

my $x = 3.1; # some variable containing a number
"$x";        # stringified
$x .= "";    # another, more awkward way to stringify
print $x;    # perl does it for you, too, quite often
             # (but for older perls)

您可以通过将其数字化来强制类型为 JSON 数字

my $x = "3"; # some variable containing a string
$x += 0;     # numify it, ensuring it will be dumped as a number
$x *= 1;     # same thing, the choice is yours.

目前,您无法通过其他更不明显的方式强制类型。

从 2.91_01 版本开始,JSON::PP 使用不同的数字检测逻辑,将可以安全转换为数字的标量转换为数字。新逻辑速度略快,并且倾向于帮助使用旧版 Perl 或想要编码复杂数据结构的人。但是,这可能会导致生成的 JSON 文本与 JSON::XS 编码的文本不同(因此可能会破坏比较整个 JSON 文本的测试)。如果您出于兼容性或更精细控制的需要,需要之前的行为,请在使用 use JSON::PP(或 JSON.pm)之前将环境变量 PERL_JSON_PP_USE_B 设置为 true。

请注意,数值精度与 Perl 中的含义相同(因此二进制到十进制的转换遵循与 Perl 相同的规则,这可能与其他语言不同)。此外,您的 Perl 解释器可能会公开平台浮点数的扩展,例如无穷大或 NaN - 这些无法在 JSON 中表示,将它们传递进去会导致错误。

JSON::PP(和 JSON::XS)信任您传递给 encode 方法(或 encode_json 函数)的是一个干净的、经过验证的数据结构,其值只能表示为有效的 JSON 值,因为它不是来自外部数据源(与您传递给 decodedecode_json 的 JSON 文本相反,JSON::PP 认为这些文本是受污染的,并且不信任)。由于 JSON::PP 不知道您和您的 JSON 文本的使用者究竟希望意外值是什么(您可能希望将它们转换为 null,或使用或不使用规范化将它们字符串化(无穷大/NaN 的字符串表示可能因平台而异),或在不转换的情况下报错),建议您在编码之前执行您和您的使用者所需的操作,并且不要将可能以看起来像数字的值(包括无穷大/NaN)开头的值数字化,而无需验证。

对象序列化

由于 JSON 无法直接表示 Perl 对象,您必须在纯 JSON 表示(无法自动再次反序列化对象)和 JSON 语法的非标准扩展(带标签的值)之间进行选择。

序列化

JSON::PP 遇到 Perl 对象时会发生什么,取决于 allow_blessedconvert_blessedallow_tagsallow_bignum 设置,这些设置按此顺序使用

1. allow_tags 启用且对象具有 FREEZE 方法。

在这种情况下,JSON::PP 会创建一个带标签的 JSON 值,使用 JSON 语法的非标准扩展。

这是通过调用对象的 FREEZE 方法实现的,第一个参数是要序列化的对象,第二个参数是常量字符串 JSON,用于将其与其他序列化器区分开来。

FREEZE 方法可以返回任意数量的值(即零个或多个)。这些值和对象的包/类名将以以下格式编码为带标签的 JSON 值

("classname")[FREEZE return values...]

例如:

("URI")["http://www.google.com/"]
("MyDate")[2013,10,29]
("ImageData::JPEG")["Z3...VlCg=="]

例如,假设的 My::Object FREEZE 方法可能会使用对象的 typeid 成员来编码对象

sub My::Object::FREEZE {
   my ($self, $serialiser) = @_;

   ($self->{type}, $self->{id})
}
2. convert_blessed 启用且对象具有 TO_JSON 方法。

在这种情况下,对象的 TO_JSON 方法将在标量上下文中调用。它必须返回一个可以直接编码为 JSON 的单个标量。此标量在 JSON 文本中替换对象。

例如,以下 TO_JSON 方法将在序列化时将所有 URI 对象转换为 JSON 字符串。这些值最初是 URI 对象这一事实将丢失。

sub URI::TO_JSON {
   my ($uri) = @_;
   $uri->as_string
}
3. allow_bignum 启用且对象是 Math::BigIntMath::BigFloat

该对象将被序列化为 JSON 数字值。

4. allow_blessed 启用。

该对象将被序列化为 JSON null 值。

5. 以上均不符合

如果未启用任何设置或缺少相应方法,JSON::PP 将抛出异常。

反序列化

对于反序列化,只有两种情况需要考虑:要么使用了非标准标记,在这种情况下 allow_tags 决定,要么对象无法自动反序列化,在这种情况下,您可以使用后处理或 filter_json_objectfilter_json_single_key_object 回调从 JSON 中获取一些真实对象。

本节仅考虑带标签的值情况:在解码过程中遇到带标签的 JSON 对象,并且 allow_tags 被禁用,将导致解析错误(就好像带标签的值不是语法的一部分一样)。

如果 allow_tags 启用,JSON::PP 将查找序列化期间使用的包/类名的 THAW 方法(它不会尝试将包加载为 Perl 模块)。如果没有这样的方法,解码将失败并出现错误。

否则,将使用类名作为第一个参数、常量字符串 JSON 作为第二个参数以及 JSON 数组中的所有值(最初由 FREEZE 方法返回的值)作为剩余参数调用 THAW 方法。

然后,该方法必须返回对象。虽然从技术上讲,您可以返回任何 Perl 标量,但您可能需要启用 allow_nonref 设置才能在所有情况下使其正常工作,因此最好返回一个实际的祝福引用。

例如,让我们实现一个 THAW 函数,该函数从前面的 FREEZE 示例中重新生成 My::Object

sub My::Object::THAW {
   my ($class, $serialiser, $type, $id) = @_;

   $class->new (type => $type, id => $id)
}

编码/代码集标志说明

本节摘自 JSON::XS。

感兴趣的读者可能已经看到了一些表示编码或代码集的标志 - utf8latin1ascii。这些标志的作用似乎存在一些混淆,因此这里简要比较一下。

utf8 控制 encode 创建的 JSON 文本(以及 decode 预期的文本)是 UTF-8 编码的还是否,而 latin1ascii 仅控制 encode 是否转义其各自代码集范围之外的字符值。这些标志之间不会相互冲突,尽管某些组合比其他组合更没有意义。

已经采取措施使所有标志在 encodedecode 方面对称,也就是说,使用任何组合的标志值编码的文本将在使用相同标志时被正确解码 - 通常,如果您在编码和解码时使用不同的标志设置,则可能在某个地方存在错误。

下面将详细讨论这些标志。请注意,“代码集”只是一个抽象的字符代码点对集合,而编码则采用这些代码点编号并对其进行编码,在本例中编码为八位字节。Unicode(除其他外)是一个代码集,UTF-8 是一种编码,而 ISO-8859-1 (= latin 1) 和 ASCII 同时是代码集编码,这可能会令人困惑。

utf8 标志禁用

utf8禁用(默认情况下)时,encode/decode会生成并期望Unicode字符串,也就是说,具有高序Unicode值(> 255)的字符将被编码为这样的字符,并且类似地,这样的字符将被解码为原样,不会对它们进行任何更改,除了将它们“重新解释”为Unicode代码点或Unicode字符(对于Perl来说,除非你做一些奇怪/奇怪/愚蠢的事情,否则它们在字符串中是相同的)。

当你想自己进行编码时(例如,当你想拥有UTF-16编码的JSON文本时)或者当其他层为你进行编码时(例如,当使用透明地编码为UTF-8的文件句柄打印到终端时,你当然不希望先对你的数据进行UTF-8编码,然后让Perl再编码一次),这很有用。

utf8标志启用

如果utf8标志启用,encode/decode将使用相应的UTF-8多字节序列对所有字符进行编码,并将期望你的输入字符串以UTF-8编码,也就是说,输入字符串的任何“字符”都不能具有任何值> 255,因为UTF-8不允许这样做。

因此,utf8标志在两种模式之间切换:禁用意味着你将在Perl中获得一个Unicode字符串,启用意味着你将在Perl中获得一个UTF-8编码的八位字节/二进制字符串。

latin1ascii标志启用

启用latin1(或ascii)后,encode将转义具有序数值> 255(ascii为> 127)的字符,并将剩余的字符编码为utf8标志指定的字符。

如果utf8禁用,则结果也会在这些字符集中正确编码(因为两者都是Unicode的适当子集,这意味着所有字符值< 256的Unicode字符串与ISO-8859-1字符串相同,而所有字符值< 128的Unicode字符串与Perl中的ASCII字符串相同)。

如果utf8启用,你仍然会得到一个正确的UTF-8编码字符串,无论这些标志如何,只是更多字符将使用\uXXXX转义,然后才转义。

请注意,ISO-8859-1编码字符串与UTF-8编码不兼容,而ASCII编码字符串则兼容。这是因为ISO-8859-1编码不是UTF-8的子集(尽管ISO-8859-1代码集是Unicode的子集),而ASCII是。

令人惊讶的是,decode会忽略这些标志,因此将所有输入值视为受utf8标志控制。如果禁用它,这将允许你解码ISO-8859-1和ASCII编码的字符串,因为它们都是Unicode的严格子集。如果启用它,你可以正确解码UTF-8编码的字符串。

因此,latin1ascii都不与utf8标志不兼容 - 它们只控制JSON输出引擎何时转义字符。

latin1 的主要用途是相对高效地将二进制数据存储为 JSON,但代价是破坏了与大多数 JSON 解码器的兼容性。

ascii 的主要用途是强制输出不包含值大于 127 的字符,这意味着您可以将结果字符串解释为 UTF-8、ISO-8859-1、ASCII、KOI8-R 或大多数其他字符集和 8 位编码,并且仍然可以获得相同的 数据结构。当您的 JSON 传输通道不是 8 位干净的,或者编码在传输过程中可能被破坏(例如在邮件中)时,这很有用,并且有效是因为 ASCII 是世界上大多数 8 位和多字节编码的适当子集。

错误

请将有关此模块特定行为的错误报告给 RT 或 GitHub 问题(优先)。

https://github.com/makamaka/JSON-PP/issues

https://rt.cpan.org/Public/Dist/Display.html?Queue=JSON-PP

至于新功能和更改常见行为的请求,请先通过电子邮件(很重要!)联系 JSON::XS 的作者(Marc Lehmann,<schmorp[at]schmorp.de>),以保持 JSON.pm 后端之间的兼容性。

一般来说,如果您需要一些特殊的东西,建议您创建一个新的模块,也许基于 JSON::Tiny,它比这个模块更小,并且以更简洁的方式编写。

另请参阅

用于快速实验的 json_pp 命令行实用程序。

JSON::XSCpanel::JSON::XSJSON::Tiny 用于更快的替代方案。 JSONJSON::MaybeXS 用于轻松迁移。

JSON::PP::Compat5005JSON::PP::Compat5006 用于较旧的 Perl 用户。

RFC4627 (http://www.ietf.org/rfc/rfc4627.txt)

RFC7159 (http://www.ietf.org/rfc/rfc7159.txt)

RFC8259 (http://www.ietf.org/rfc/rfc8259.txt)

作者

Makamaka Hannyaharamitu,<makamaka[at]cpan.org>

当前维护者

Kenichi Ishigaki,<ishigaki[at]cpan.org>

版权和许可

版权所有 2007-2016 Makamaka Hannyaharamitu

大多数文档来自 Marc Lehmann 的 JSON::XS

该库是免费软件;您可以根据与 Perl 本身相同的条款重新分发它和/或修改它。