JSON encodeでhtmlタグのエスケープ

encodeした時にhtmlタグの「<」「>」を「<」→「\u003c」「>」→「\u003e」に変換したい。

使ってるのはpure perl版のJSON::PPで、encodeする時のオプションは以下。

  • -canonical(1)
    出力されるJSON文字列の各要素の順番をキーの辞書順に統一するため
  • -indent(0)
    インデント不要
  • -space_before(0), -space_after(0)
    キーと値の間に空白は不要

my $json = JSON->new->canonical(1)->indent(0)->space_before(0)->space_after(0);
	

perl の hashをJSON encodeに渡して文字列にする(serialize)と「"」などは「\"」とエスケープ処理されるけど「<」や「>」はそのまま。
検索したところ、JSON encode(PP版)にはhtmlのタグをエスケープする方法がないらしい。

解決方法としては、encodeしてJSON文字列してから、正規表現を使って置換する、という力技。

JSON文字列すべてに対して正規表現で「<」「>」をひっかけて変換して間違えて、JSONとして使えなくなると困る。

ActivtyPubで使うためのJSONで、htmlタグのエスケープが必要なところは(ほとんど)「content」だけ。


sub json_str{
    my $self = shift;
    my $args = shift;

    return if ! $args->{ref};

    my $json = JSON->new->canonical(1)->indent(0)->space_before(0)->space_after(0);
    my $str;
    eval{ $str = $json->encode( $args->{ref} ) };
    if( $@ ){
        printf qq{ERROR json_str :: activity.pm --- %s\n}, $@;
        exit;
    }
    my $esc = sub{ my $str = shift;
        $str =~ s!<!\\u003c!g; $str =~ s!>!\\u003e!g;
        return $str;
    };
    $str =~ s!"content":"((?:\\.|[^"\\])*)"!sprintf(qq{"content":"%s"}, $esc->($1))!ge;
    return $str;
}
	

encodeはダブルクォートをエスケープする。「\"」を考慮した正規表現がちょっと面倒くさい。

(エスケープ処理は不特定多数のユーザーが書き込むような場合。ウチみたいにひとりしか使ってないようなところは問題にならない)

[2026-03-22 09:01:42] v1.0.0

Menu