Editing And Printing

JsonRecast is designed for small, intentional edits to existing JSON files. Changed nodes are printed again; untouched nodes keep their original text.

Contents

  1. Replace Object Values
  2. Add Or Update Object Keys
  3. Remove Object Items
  4. Edit Arrays
  5. Remove Empty Parents
  6. Preserved Output Example

Replace Object Values

Match an ObjectItemNode when you want both the key and the current value.

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

$document = JsonRecast::parse(<<<'JSON'
{
    "name" : "acme/demo",
    "type" : "library"
}
JSON);

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

            return $node;
        }

        return null;
    }
});

echo JsonRecast::print($result);
{
    "name" : "boundwize/jsonrecast",
    "type" : "library"
}

The spaces around the colon remain because the object item was reprinted with its parsed trivia.

Add Or Update Object Keys

ObjectNode::set() updates an existing key or appends a new item when the key is missing.

use Boundwize\JsonRecast\Node\NodeJson;
use Boundwize\JsonRecast\Node\ObjectNode;
use Boundwize\JsonRecast\NodePath\NodeJsonPath;
use Boundwize\JsonRecast\NodeVisitor\NodeJsonVisitorAbstract;
use Boundwize\JsonRecast\Value\JsonValue;

$result = JsonRecast::traverse($document, new class extends NodeJsonVisitorAbstract {
    public function leaveNode(NodeJson $node, NodeJsonPath $path): ?NodeJson
    {
        if (! $node instanceof ObjectNode || ! $path->isRoot()) {
            return null;
        }

        $node->set('license', JsonValue::from('MIT'));

        return $node;
    }
});

Returning $node is important. It records the object in the change set so the preserving printer knows a new child was added.

Remove Object Items

Return NodeJsonVisitor::REMOVE_NODE from an object item visitor to remove the whole key/value pair.

use Boundwize\JsonRecast\NodeVisitor\NodeJsonVisitor;

public function enterNode(NodeJson $node, NodeJsonPath $path): null|NodeJson|int
{
    if (
        $node instanceof ObjectItemNode
        && $path->isRoot()
        && $node->key->value === 'minimum-stability'
    ) {
        return NodeJsonVisitor::REMOVE_NODE;
    }

    return null;
}

Use ObjectNode::remove('key') when you are already editing a parent object and want to remove a key by name.

Edit Arrays

ArrayNode exposes helpers for the common operations:

use Boundwize\JsonRecast\Node\ArrayNode;
use Boundwize\JsonRecast\Value\JsonValue;

$array->append(JsonValue::from('added'));
$array->insert(1, JsonValue::from('middle'));
$array->removeAt(0);

You can also remove an array item by returning NodeJsonVisitor::REMOVE_NODE from an ArrayItemNode visitor.

use Boundwize\JsonRecast\Node\ArrayItemNode;
use Boundwize\JsonRecast\Node\StringNode;
use Boundwize\JsonRecast\NodeVisitor\NodeJsonVisitor;

public function enterNode(NodeJson $node, NodeJsonPath $path): null|NodeJson|int
{
    if (
        $node instanceof ArrayItemNode
        && $node->value instanceof StringNode
        && $node->value->value === 'temporary'
    ) {
        return NodeJsonVisitor::REMOVE_NODE;
    }

    return null;
}

Array paths are reindexed after removals and insertions, so later visitors see the current array positions.

Remove Empty Parents

Use leaveNode() when a parent decision depends on child edits that have already happened.

use Boundwize\JsonRecast\Node\ArrayNode;
use Boundwize\JsonRecast\Node\ObjectItemNode;
use Boundwize\JsonRecast\Node\ObjectNode;
use Boundwize\JsonRecast\NodeVisitor\NodeJsonVisitor;

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 NodeJsonVisitor::REMOVE_NODE;
    }

    return null;
}

Preserved Output Example

Input:

{
    "name": "acme/demo",
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    },
    "minimum-stability": "dev"
}

After changing name, adding Boundwize\\JsonRecast\\, and removing minimum-stability, the printer rewrites only the affected pieces:

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