Boarking
Boarking

Reputation: 95

PHP - compare the structure of two JSON objects

I have two JSON objects and I would like to compare their structure. How can I do it?

Those object are being generated on-the-fly and depending on dynamic content. Which means that the objects are always different but most of the time have the same structure. I want to be able to catch the changes once they occur.

Example: These two objects should be considered as equal, because both have the same structure: index var and tags array.

{
    "index": 0,
    "tags": [
        "abc"
    ]
}
{
    "index": 1,
    "tags": [
        "xyz"
    ]
}

Thoughts?

Upvotes: 7

Views: 11954

Answers (5)

domis86
domis86

Reputation: 1357

You can try to use package https://github.com/coduo/php-matcher

Example: These two objects should be considered as equal, because both have the same structure: index var and tags array.

You can create a "php-matcher pattern" like this:

{
    "index": "@integer@",
    "tags": "@[email protected](\"@string@\")"
}

Then you match your JSONs against this pattern. If you have 2 JSONs and both match this pattern then it means that they are "equal" according to your definition of equality above.

Please see results in "php-matcher sandbox" for the example JSONs you gave:

Example 1 in sandbox

Example 2 in sandbox

Additionally you can use package https://github.com/sebastianbergmann/diff (which you should already have if you have phpunit) to generate a diff when the pattern doesnt match the value.

For example:

use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;

...
    $valueToCheck = '{
        "foo": 0,
        "bar": {"one": 1, "two": "2"}
    }';
    $expectedValuePattern = '{
        "foo": "@integer@",
        "bar": {"one": 1, "two": 2}
    }';

    if (!$matcher->match($valueToCheck, $expectedValuePattern)) {
        $differ = new Differ(
            new UnifiedDiffOutputBuilder(
                "Json value is not matching expected format:\n",
                true
            )
        );
        $diffOutput = $differ->diff(
            \json_encode(\json_decode($expectedValuePattern, true), JSON_PRETTY_PRINT),
            \json_encode(\json_decode($valueToCheck, true), JSON_PRETTY_PRINT)
        );

        var_dump(
            $diffOutput
            . "\n".$matcher->getError()."\n"
        );
    } else {
        var_dump('OK');
    }

it will print:

Json value is not matching expected format:
@@ -1,7 +1,7 @@
 {
-    "foo": "@integer@",
+    "foo": 0,
     "bar": {
         "one": 1,
-        "two": 2
+        "two": "2"
     }
 }

That message with diff is especially helpfull for bigger JSON's to quickly see which element is not matching.

See more ways of usage in README of that package - especially:

https://github.com/coduo/php-matcher#json-matching

https://github.com/coduo/php-matcher#json-matching-with-unbounded-arrays-and-objects

This package is very good to use in automatic tests (for example: phpunit) to assert if JSON from API responses is correct etc - considering that in integration tests there are often many id's, uuid's, datetime's etc which change on each test execution - like database generated id's etc.

I hope it helps :)

Upvotes: 1

Lucas Cordeiro
Lucas Cordeiro

Reputation: 213

## You can use this library TreeWalker php .##

TreeWalker is a simple and smal API in php
(I developed this library, i hope it helps you)

It offers two methods
1- Get json difference
2- Edit json value (Recursively)

this method will return the diference between json1 and json2

$struct1 = array("casa"=>1, "b"=>"5", "cafeina"=>array("ss"=>"ddd"), "oi"=>5);
$struct2 = array("casa"=>2, "cafeina"=>array("ss"=>"dddd"), "oi2"=>5);

//P.s
print_r($treeWalker->getdiff($struct1, $struct2))

{
    new: {
        b: "5",
        oi: 5
    },
    removed: {
        oi2: 5
    },
    edited: {
        casa: {
          oldvalue: 2,
          newvalue: 1
        },
        cafeina/ss: {
          oldvalue: "dddd",
          newvalue: "ddd"
        }
    },
    time: 0
}

Upvotes: 13

0yeoj
0yeoj

Reputation: 4550

You mean by structure, like model array like:

array ( 'index' => int, 'tags' =>  array() )

If that's what you are trying to get, try this...

$arr1 = array (
    array (
        'index' => 0,
        'tags' =>  ['abc']
    ),
    array (
        'index' => 1,
        'tags' =>  ['xyz']
    ),
    array (
        'index' => 2,
        'tags' =>  ['xyz'],
        'boom' =>  'granade'
    ),
    array (
        'index' => 3,
        'tags' =>  'xyz'
    )
);

$holder = array();

$model  = array ('index' => 0, 'tags' => array());

for ($i = 0;$i < count($arr1); $i++)
{
    $holder = array_diff(array_merge_recursive($arr1[$i], $model), $model);

    if (!empty($holder))
    {
        echo "different structure<br>";
    }
    else
    {
        echo "same structure<br>";

        // for further validation
        /*
        $keys = array_keys($model);

        if (is_int($arr1[$i][$keys[0]]) && is_array($arr1[$i][$keys[1]]))
            echo "same structure<br>";
        else
            echo "different structure<br>";
        */
    }

}

Sample output:

same structure
same structure
different structure
different structure

Upvotes: 0

vonUbisch
vonUbisch

Reputation: 1469

It's a bit rough, but you get the picture;

$json = '[
        {
            "index": 0,
            "tags": [
                "abc"
            ]
        },
        {
            "index": 1,
            "tags": [
                "xyz"
            ]
        },
        {
            "foo": 2,
            "bar": [
                "xyz"
            ]
        }]';

$array = json_decode($json, true);
$default = array_keys($array[0]);

$error = false;
$errors = array();
foreach ($array as $index => $result):
    foreach ($default as $search):
        if (!isset($result[$search])):
            $error = true;
            $errors[] = "Property '{$search}' at entry '{$index}' not found. ";
        endif;
    endforeach;
endforeach;

if ($error):
    echo 'Objects are not the same. ';
    foreach ($errors as $message):
        echo $message;
    endforeach;
endif;

returns:

Objects are not the same. Property 'index' at entry '2' not found. Property 'tags' at entry '2' not found.

Upvotes: 1

ismnoiet
ismnoiet

Reputation: 4169

You can convert the json string to a php array then use the array_diff($arr1,$arr2) function to compare the newly created array with another array the result is an array containing the elements of the first array that doesn't exist in the other array

example :

<?php 
    $array1 = '{"name":"myname","age":"40"}';
    //convert the obtained stdclass object to an array
    $array1 = (array) json_decode($array1); 



    $array2 = array("name"=>"myname123","age"=>10);

    print_r($array2);

    $result_array = array_diff($array1,$array2);


    if(empty($result_array[0])){     
        echo "they have the same structure ";
    }



?>

Upvotes: -1

Related Questions