7

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?

4
  • so you want to check if object 1 has the same fields as object 2? Commented Aug 17, 2015 at 8:24
  • Yes, exactly. I tried to use RecursiveArrayIterator::hasChildren() in order to iterate over leaves only but this solution does not seem to me an elegant one. May be someone knows a better way? Commented Aug 17, 2015 at 8:42
  • @Boarking, did you get it working? Commented Aug 17, 2015 at 9:41
  • maybe useful? compare object properties and show diff in PHP Commented Aug 17, 2015 at 11:13

5 Answers 5

13

## 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
}
Sign up to request clarification or add additional context in comments.

2 Comments

I used this library on my current work project - worked great for my purposes (needed a diff). Two thumbs up
I just used this package as well and it works great. Saved me a lot of time, thank you!
1

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.

3 Comments

The problem here is with the default array. How can I generate it? All I have is two JSON objects. array_keys() is also not a good option because it is not recursive. My actual objects are very big and have many levels of nesting. I have to be able to compare everything.
Is it not possible to expect a certain structure? As far as I know, to do this efficiently you will need to expect something.
I have no idea of what I am expecting. All I know is that I have two JSON objects which likely the same but I have to be sure before I proceed.
1

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 :)

Comments

0

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

Comments

-1

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 ";
    }



?>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.