pwm/jgami

Map functions over JSON.

0.5.0 2018-10-26 19:22 UTC

This package is auto-updated.

Last update: 2024-04-27 09:06:56 UTC


README

Build Status codecov Test Coverage License: MIT

Have you ever mapped a function over a list? Now you can do the same with JSON!

JGami provides a simple API to update values in arbitrarily complex JSON structures while preserving structural integrity.

Table of Contents

Requirements

PHP 7.2+

Installation

$ composer require pwm/jgami

Usage

It's probably easiest to dive straight into some sample code. We will update some JSON data to our liking by mapping a function over it:

// What we have
$json = '{
    "name": "Alice",
    "age": 27,
    "likes": [
        "Types",
        "Graphs",
        "Nature"
    ],
    "job": {
        "title": "Developer",
        "company": "Acme Corp."
    },
    "pets": [
        {
            "name": ["Woof"],
            "type": "Dog"
        },
        {
            "name": ["Mr. Grumpy", "Mrs. Grumpy"],
            "type": "Cat"
        }
    ]
}';

// What we want
$expectedJson = '{
    "name": "Alice Wonderland",
    "age": 37,
    "likes": [
        "Types",
        "Trees",
        "Nature"
    ],
    "job": {
        "title": "Developer",
        "company": "Acme Corp."
    },
    "pets": [
        {
            "name": ["Woof <3"],
            "type": "Dog"
        },
        {
            "name": ["Mr. Grumpy <3", "Mrs. Grumpy <3"],
            "type": "Cat"
        }
    ]
}';

// Our update function, to be mapped over our JSON data
// It takes a Node and return a JVal ie. a json value
$f = function (Node $node): JVal {
    $jVal = $node->jVal();
    if ($jVal->val() === 'Graphs') {
        // Replace "Graphs" with "Trees"
        $jVal = new JString('Trees');
    } elseif ($node->key()->eq('age')) {
        // Add 10 to values with key "age"
        $jVal = new JInt($jVal->val() + 10);
    } elseif ($node->path()->hasAll('pets', 'name')) {
        // Add " <3" to values in paths with "pets" and "name"
        $jVal = new JString($jVal->val() . ' <3');
    } elseif ($node->path()->hasNone('pets') && $node->key()->eq('name')) {
        // Add " Wonderland" to values with key "name" that has no "pets" in their path
        $jVal = new JString($jVal->val() . ' Wonderland');
    }
    return $jVal;
};

// true
assert(
    json_encode(json_decode($expectedJson))
    ===
    json_encode(JGami::map($f, json_decode($json)))
);

Modification of values is restricted to leaf nodes. This preserves structural integrity. The only exception is extending leaf nodes as seem by the following example:

$json = '{
    "metadata": "To be filled"
}';

$expectedJson = '{
    "metadata": {
        "species": "Human",
        "planet": "Earth",
        "galacticLevel": 3,
        "note": "Observe only"
    }
}';

$f = function (Node $node): JVal {
    $jVal = $node->jVal();
    if ($node->key()->eq('metadata')) {
        // Metadata, provided by our galactic overlords
        $val = O::from([
            'species'       => 'Human',
            'planet'        => 'Earth',
            'galacticLevel' => 3,
            'note'          => 'Observe only',
        ]);
        // Replace the existing string value with the above object
        return new JObject($val);
    }
    return $jVal;
};
// true
assert(
    json_encode(json_decode($expectedJson))
    ===
    json_encode(JGami::map($f, json_decode($json)))
);

This preserves existing structure while extending it with a new substructure.

How it works

TBD

Tests

$ composer utest
$ composer phpcs
$ composer phpstan
$ composer infection

Changelog

Click here

Licence

MIT