apimatic / jsonmapper
Map nested JSON structures onto PHP classes
Installs: 4 637 922
Dependents: 64
Suggesters: 0
Security: 0
Stars: 22
Watchers: 5
Forks: 184
Open Issues: 7
Requires
- php: ^5.6 || ^7.0 || ^8.0
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
- squizlabs/php_codesniffer: ^3.0.0
- dev-master
- 3.1.4
- 3.1.3
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.7
- 3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.2
- v1.0.1
- v1.0.0
- v0.10.0
- v0.9.0
- v0.8.0
- v0.7.0
- v0.6.1
- v0.6.0
- v0.5.0
- v0.4.4
- v0.4.3
- v0.4.2
- v0.4.1
- v0.4.0
- v0.3.0
- v0.2.1
- v0.2.0
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
- dev-merge-release-notification
- dev-release-notification
This package is auto-updated.
Last update: 2024-10-12 05:14:42 UTC
README
Takes data retrieved from a JSON web service and converts them into nested object and arrays - using your own model classes.
Starting from a base object, it maps JSON data on class properties, converting them into the correct simple types or objects.
It's a bit like the native SOAP parameter mapping PHP's SoapClient
gives you, but for JSON.
Note that it does not rely on any schema, only your class definitions.
Type detection works by parsing @var
docblock annotations of
class properties, as well as type hints in setter methods. If docblock comments,
or comments in general are discarded through some configuration setting like opcache.save_comments=0
,
or any other similar configuration, an exception is thrown, blocking any further operation.
You do not have to modify your model classes by adding JSON specific code; it works automatically by parsing already-existing docblocks.
Keywords: deserialization, hydration
Contents
Pro & contra
Benefits
- Autocompletion in IDEs
- It's easy to add comfort methods to data model classes
- Your JSON API may change, but your models can stay the same - not breaking applications that use the model classes.
Drawbacks
Model classes need to be written by hand
Since JsonMapper does not rely on any schema information (e.g. from json-schema), model classes cannot be generated automatically.
Usage
Basic usage
- Register an autoloader that can load PSR-0 compatible classes.
- Create a
JsonMapper
object instance - Call the
map
ormapArray
method, depending on your data
Map a normal object:
<?php require 'autoload.php'; $mapper = new JsonMapper(); $contactObject = $mapper->map($jsonContact, new Contact()); ?>
Map an array of objects:
<?php require 'autoload.php'; $mapper = new JsonMapper(); $contactsArray = $mapper->mapArray( $jsonContacts, new ArrayObject(), 'Contact' ); ?>
Example
JSON from a address book web service:
{ 'name':'Sheldon Cooper', 'address': { 'street': '2311 N. Los Robles Avenue', 'city': 'Pasadena' } }
Your local Contact
class:
<?php class Contact { /** * Full name * @var string */ public $name; /** * @var Address */ public $address; } ?>
Your local Address
class:
<?php class Address { public $street; public $city; public function getGeoCoords() { //do something with the $street and $city } } ?>
Your application code:
<?php $json = json_decode(file_get_contents('http://example.org/bigbang.json')); $mapper = new JsonMapper(); $contact = $mapper->map($json, new Contact()); echo "Geo coordinates for " . $contact->name . ": " . var_export($contact->address->getGeoCoords(), true); ?>
Letting JsonMapper create the instances for you
Map a normal object (works similarly to map
):
$mapper = new JsonMapper(); $contactObject = $mapper->mapClass($jsonContact, 'Contact');
Map an array of objects (works similarly to mapArray
):
$mapper = new JsonMapper(); $contactsArray = $mapper->mapClassArray($jsonContacts, 'Contact');
Map a value with any combination of types e.g oneOf(string,int) or anyOf(string,Contact):
$mapper = new JsonMapper(); $contactObject = $mapper->mapFor($value, 'oneOf(string,Contact)');
Property type documentation
JsonMapper
uses several sources to detect the correct type of
a property:
The setter method (
set
+ucwords($propertyname)
) is inspected.Underscores make the next letter uppercase, which means that for a JSON property
foo_bar_baz
a setter method ofsetFooBarBaz
is used.If it has a type hint in the method signature, this type used:
public function setPerson(Contact $person) {...}
The method's docblock is inspected for
@param $type
annotations:/** * @param Contact $person Main contact for this application */ public function setPerson($person) {...}
If no type could be detected, the plain JSON value is passed to the setter method.
@var $type
docblock annotation of class properties:/** * @var \my\application\model\Contact */ public $person;
Note that the property has to be public to be used directly.
If no type could be detected, the property gets the plain JSON value.
If a property can not be found, JsonMapper tries to find the property in a case-insensitive manner. A JSON property
isempty
would then be mapped to a PHP propertyisEmpty
.
To map a JSON key to an arbitrarily named class property, you can use
the @maps
annotation:
/** * @var \my\application\model\Person * @maps person_object */ public $person;
Supported type names:
- Simple types:
string
bool
,boolean
int
,integer
float
array
object
- Class names, with and without namespaces
- Arrays of simple types and class names:
int[]
Contact[]
- ArrayObjects of simple types and class names:
ContactList[Contact]
NumberList[int]
- Nullable types:
int|null
- will benull
if the value in JSON isnull
, otherwise it will be an integer
ArrayObjects and extending classes are treated as arrays.
Variables without a type or with type mixed
will get the
JSON value set directly without any conversion.
See phpdoc's type documentation for more information.
Simple type mapping
When an object shall be created but the JSON contains a simple type only (e.g. string, float, boolean), this value is passed to the classes' constructor. Example:
PHP code:
/** * @var DateTime */ public $date;
JSON:
{"date":"2014-05-15"}
This will result in new DateTime('2014-05-15')
being called.
Custom property initialization
You can use the @factory
annotation to specify a custom method that
will be called to get the value to be assigned to the property.
/** * @factory MyUtilityClass::createDate */ public $date;
Here, createDate
method in the MyUtilityClass
is called with the
raw value for date
property and the value returned by the factory method
is then assigned to the date
property.
The factory method should return true when tested with is_callable
, otherwise
an exception will be thrown.
The factory annotation can be used with other annotations such as @var
; however,
only the value created by the factory method will be used while other typehints and
initialization methods for the property will be ignored.
Logging
JsonMapper's setLogger()
method supports all PSR-3 compatible
logger instances.
Events that get logged:
- JSON data contain a key, but the class does not have a property or setter method for it.
- Neither setter nor property can be set from outside because they are protected or private
Handling invalid or missing data
During development, APIs often change. To get notified about such changes, JsonMapper may throw exceptions in case of either missing or yet unknown data.
Unknown properties
When JsonMapper sees properties in the JSON data that are
not defined in the PHP class, you can let it throw an exception
by setting $bExceptionOnUndefinedProperty
:
$jm = new JsonMapper(); $jm->bExceptionOnUndefinedProperty = true; $jm->map(...);
To process unknown properties yourself, you can set a method on the class as a collection method:
$jm = new JsonMapper(); $mapper->sAdditionalPropertiesCollectionMethod = 'addAdditionalProperty'; $jm->map(...);
Here, the addAdditionalProperty()
method will be called with a name
and
a value
argument.
Missing properties
Properties in your PHP classes can be marked as "required" by
putting @required
in their docblock:
/** * @var string * @required */ public $someDatum;
When the JSON data do not contain this property, JsonMapper will throw
an exception when $bExceptionOnMissingData
is activated:
$jm = new JsonMapper(); $jm->bExceptionOnMissingData = true; $jm->map(...);
Passing arrays to map()
You may wish to pass array data into map()
that you got by calling
json_decode($jsonString, true)
By default, JsonMapper will throw an exception because map()
requires
an object as first parameter.
You can circumvent that by setting $bEnforceMapType
to false
:
$jm = new JsonMapper(); $jm->bEnforceMapType = false; $jm->map(...);
Handling polymorphic responses
JsonMapper allows you to map a JSON object to a derived class based on a discriminator field. The discriminator field's value is used to decide which class this JSON object should be mapped to.
Your local Person
class:
<?php /** * @discriminator type * @discriminatorType person */ class Person { public $name; public $age; public $type; }
Your local Employee
class:
<?php /** * @discriminator type * @discriminatorType employee */ class Employee extends Person { public $employeeId; }
Your application code:
$mapper = new JsonMapper(); $mapper->arChildClasses['Person'] = ['Employee']; $mapper->arChildClasses['Employee'] = []; $person = $mapper->mapClass($json, 'Person');
Now, if the value of the type
key in JSON is "person"
then an instance of
a Person
class is returned. However, if the type
is "employee"
then
an instance of Employee
class is returned.
Classes need to be registered in arChildClasses
before being used with
discriminator.
Note that there can only be one discriminator field in an object hierarchy.
Polymorphic responses also work if the polymorphic class is embedded as a field or in an array.
To map an array of classes, use the mapArrayClass
which will create the right
type of objects by examining the discriminatorType
value.
Installation
Supported PHP Versions
- PHP 5.6
- PHP 7.0
- PHP 7.1
- PHP 7.2
- PHP 7.4
- PHP 8.0
- PHP 8.1
- PHP 8.2
Install the Package
From Packagist:
$ composer require apimatic/jsonmapper
Related software
- Jackson's data binding for Java
- Johannes Schmitt Serializer for PHP
About JsonMapper
License
JsonMapper is licensed under the OSL 3.0.
Coding style
JsonMapper follows the PEAR Coding Standards.