使用JSON对象时出现“Modification of a read-only value attempted”错误排查
Perl JSON代码在SLES15 SP7中出现只读值修改错误的原因与解决
问题背景
几年前编写的Perl JSON代码在SLES15 SP6中正常运行,升级到SLES15 SP7后出现以下错误:
Modification of a read-only value attempted at (eval 73)[/usr/lib/perl5/vendor_perl/5.26.1/JSON.pm:287] line 63.
涉事业务代码:
use JSON -support_by_pp; #... use constant JSON_INDENT => JSON->new(); # indenting JSONizer JSON_INDENT->utf8(1); JSON_INDENT->indent(1); JSON_INDENT->indent_length(0);
注释最后一行JSON_INDENT->indent_length(0);后错误消失,代码中使用该常量的方式为:print JSON_INDENT->pretty->encode($JSON), "\n"。
SP升级时的相关包变更:
- 新增包
perl-JSON-XS-4.40.0-bp157.2.3.1.x86_64 perl-Cpanel-JSON-XS从4.37-bp156.2.3.1升级到4.380.0-150700.3.3.1(x86_64架构)
JSON.pm中涉事代码片段(第287行为eval $data;):
sub _load_xs { my ($module, $opt) = @_; __load_xs($module, $opt) or return; my $data = join("", <DATA>); # this code is from Jcode 2.xx. close(DATA); eval $data; JSON::Backend::XS->init($module); return 1; };
问题原因
- 常量的只读特性:Perl中
use constant定义的常量属于只读值,即便JSON对象是引用类型,XS后端的实现逻辑会在修改对象属性时触发对常量本身的只读检查。 - 后端校验强化:升级后的XS后端(
perl-Cpanel-JSON-XS或新增的perl-JSON-XS)对对象属性修改的校验更严格。SP6中使用的PP后端(纯Perl实现)未严格限制这一操作,而XS后端在新版本中强化了只读限制,导致修改常量引用的对象属性时抛出错误。 - indent_length的触发逻辑:
indent_length的修改逻辑在XS后端中直接关联到常量值的操作,刚好触发了只读检查,而utf8、indent等方法因实现方式不同未触发错误。
解决方法
方案1:替换constant为普通变量
将只读常量改为普通变量,规避只读限制:
use JSON -support_by_pp; #... my $JSON_INDENT = JSON->new(); $JSON_INDENT->utf8(1); $JSON_INDENT->indent(1); $JSON_INDENT->indent_length(0);
使用时改为:print $JSON_INDENT->pretty->encode($JSON), "\n"
方案2:链式调用初始化对象
创建JSON对象时直接链式调用配置方法,一次性完成属性设置,避免后续修改常量:
use constant JSON_INDENT => JSON->new()->utf8(1)->indent(1)->indent_length(0);
方案3:强制使用PP后端
如果必须保留常量定义,可强制JSON模块使用纯Perl后端,绕过XS的严格检查:
use JSON -support_by_pp; # 或手动指定后端 $JSON::Backend = 'JSON::PP';
内容的提问来源于stack exchange,提问作者U. Windl
相关产品推荐
相关产品推荐

