承接 boundwize/jsonrecast 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

boundwize/jsonrecast

Composer 安装命令:

composer require boundwize/jsonrecast

包简介

Editable JSON AST with visitor traversal and formatting-preserving printing.

README 文档

README

JsonRecast Logo

A PHP JSON parser that turns JSON into an editable AST, supports visitor-based traversal, and prints changes back while preserving the original formatting.

Inspired by PHP-Parser, built for tools that need to modify JSON files safely.

JsonRecast is optimized for safely changing files. It keeps the original structure and formatting where possible, so automated tools can modify JSON without creating noisy diffs.

The AST stays clean. Change metadata lives in the traversal result.

Latest Version ci build Code Coverage PHPStan Downloads

Windows macOS Linux

Installation

composer require boundwize/jsonrecast

Features

  • Parse JSON into an AST
  • Traverse and modify nodes with NodeJsonTraverser
  • Create visitors with NodeJsonVisitor
  • Access runtime traversal context with NodeJsonPath
  • Replace, add, and remove JSON data
  • Preserve original formatting when printing modified JSON
  • Keep number representations like 1, 1.0, and 1e0
  • Supports recursive objects and arrays
  • Tracks changes outside the AST
  • Dump the AST for tooling and debugging
  • Designed for tooling, config updates, and automated refactoring

Quick Start

Start by parsing JSON into a document node:

use Boundwize\JsonRecast\JsonRecast;

$jsonContent = '{"name": "jsonrecast", "private": true}';
$document    = JsonRecast::parse($jsonContent);

The result is an AST. Dump it when you want to understand the node shape your visitors will receive:

echo JsonRecast::dumpAst($document);
JsonDocument
  value: ObjectNode
    items:
      [0]: ObjectItemNode
        key: StringNode(value: "name")
        value: StringNode(value: "jsonrecast")
      [1]: ObjectItemNode
        key: StringNode(value: "private")
        value: BooleanNode(value: true)

The structure follows the JSON shape:

  • JsonDocument wraps the root JSON value
  • ObjectNode and ArrayNode contain item nodes
  • ObjectItemNode contains a string key and a value node
  • Scalar values use StringNode, NumberNode, BooleanNode, and NullNode

Arrays use the same pattern with ArrayItemNode:

$document = JsonRecast::parse('["json", 1, null]');

echo JsonRecast::dumpAst($document);
JsonDocument
  value: ArrayNode
    items:
      [0]: ArrayItemNode
        value: StringNode(value: "json")
      [1]: ArrayItemNode
        value: NumberNode(rawValue: "1")
      [2]: ArrayItemNode
        value: NullNode

After traversal, the dumper also accepts JsonRecastResult:

$result = JsonRecast::traverse($document, $visitor);

echo JsonRecast::dumpAst($result);

Pass a named option when you need parser metadata:

echo JsonRecast::dumpAst($document, includeAttributes: true);

You can also use the utility directly:

use Boundwize\JsonRecast\AstDumper;

echo (new AstDumper(includeAttributes: true))->dump($document);

Editing and Printing

Given this JSON:

{
    "name": "acme/demo",
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/Fixtures/App"
        ]
    },
    "minimum-stability": "dev"
}

You can edit name, add a PSR-4 namespace, and delete stale object or array data in one traversal:

use Boundwize\JsonRecast\JsonRecast;
use Boundwize\JsonRecast\Node\ArrayNode;
use Boundwize\JsonRecast\Node\NodeJson;
use Boundwize\JsonRecast\Node\ObjectItemNode;
use Boundwize\JsonRecast\Node\ObjectNode;
use Boundwize\JsonRecast\Node\StringNode;
use Boundwize\JsonRecast\NodePath\NodeJsonPath;
use Boundwize\JsonRecast\NodeVisitor\NodeJsonVisitor;
use Boundwize\JsonRecast\NodeVisitor\NodeJsonVisitorAbstract;

$document = JsonRecast::parse($json);

$result = JsonRecast::traverse($document, new class extends NodeJsonVisitorAbstract {
    public function enterNode(NodeJson $node, NodeJsonPath $path): null|NodeJson|int
    {
        if (
            $node instanceof ObjectItemNode
            && $path->isRoot()
        ) {
            if ($node->key->value === 'name') {
                $node->value = new StringNode('boundwize/jsonrecast');

                return $node;
            }

            if ($node->key->value === 'minimum-stability') {
                return NodeJsonVisitor::REMOVE_NODE;
            }
        }

        if ($node instanceof ObjectNode && $path->matches(['autoload', 'psr-4'])) {
            $node->set('Boundwize\\JsonRecast\\', new StringNode('src/'));

            return $node;
        }

        if ($node instanceof ArrayNode && $path->matches(['autoload-dev', 'classmap'])) {
            $removed = false;

            foreach ($node->items as $index => $item) {
                if (! $item->value instanceof StringNode || $item->value->value !== 'tests/Fixtures/App') {
                    continue;
                }

                $node->removeAt($index);
                $removed = true;
            }

            return $removed ? $node : null;
        }

        return null;
    }
});

echo JsonRecast::print($result);

The printed JSON keeps the surrounding formatting and only rewrites the changed pieces:

{
    "name": "boundwize/jsonrecast",
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Boundwize\\JsonRecast\\": "src/"
        }
    },
    "autoload-dev": {
        "classmap": [
        ]
    }
}

Use leaveNode() when a parent decision depends on child nodes that may already have changed. For example, after the classmap array item is removed, you can remove the now-empty root autoload-dev item:

// ...
    public function leaveNode(NodeJson $node, NodeJsonPath $path): ?int
    {
        if (
            ! $node instanceof ObjectItemNode
            || ! $path->isRoot()
            || $node->key->value !== 'autoload-dev'
            || ! $node->value instanceof ObjectNode
        ) {
            return null;
        }

        $classmapItem = $node->value->get('classmap');

        if (
            ! $classmapItem instanceof ObjectItemNode
            || ! $classmapItem->value instanceof ArrayNode
            || $classmapItem->value->items !== []
        ) {
            return null;
        }

        return NodeJsonVisitor::REMOVE_NODE;
    }
// ...

With that hook added, the printed JSON becomes:

{
    "name": "boundwize/jsonrecast",
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Boundwize\\JsonRecast\\": "src/"
        }
    }
}

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-28