Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Map nested JSON structures onto PHP classes

License

NotificationsYou must be signed in to change notification settings

cweiske/jsonmapper

Repository files navigation

Takes data retrieved from aJSON web service and converts theminto 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'sSoapClientgives you, but for JSON.It does not rely on any schema, only your PHP class definitions.

Type detection works by parsing type declarations and@vardocblock annotations of class properties,as well as type hints in setter methods.

You do not have to modify your model classes by adding JSON specific code;it works automatically by parsing already-existing docblocks.

This library has no dependencies.

Keywords: deserialization, hydration

  • 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 - notbreaking applications that use the model classes.
  • Model classes need to be written by hand

    Since JsonMapper does not rely on any schema information(e.g. fromjson-schema), model classes cannot be generatedautomatically.

  1. Installnetresearch/jsonmapper with composer
  2. Create aJsonMapper object instance
  3. Call themap ormapArray method, depending on your data

Map a normal object:

<?phprequire'autoload.php';$mapper =newJsonMapper();$contactObject =$mapper->map($jsonContact,newContact());// or as classname$contactObject =$mapper->map($jsonContact, Contact::class);

Map an array of objects:

<?phprequire'autoload.php';$mapper =newJsonMapper();$contactsArray =$mapper->mapArray($jsonContacts,array(),'Contact');

Instead ofarray() you may also useArrayObject and derived classes,as well as classes implementingArrayAccess.

JSON from an address book web service:

{"name":"Sheldon Cooper","address":{"street":"2311 N. Los Robles Avenue","city":"Pasadena"}}

Your localContact class:

<?phpclass Contact{/**     * Full name     */publicstring$name;public ?Address$address;}

Your localAddress class:

<?phpclass Address{public$street;public$city;publicfunctiongetGeoCoords()    {//do something with $street and $city    }}

Your application code:

<?php$json =json_decode(file_get_contents('http://example.org/sheldon.json'));$mapper =newJsonMapper();$contact =$mapper->map($json,newContact());echo"Geo coordinates for" .$contact->name .":"    .var_export($contact->address->getGeoCoords(),true);

JsonMapper uses several sources to detect the correct type ofa property in the following order:

  1. Setter method (set +ucwords($propertyname))

    Underscores "_" and hyphens "-" make the next letter uppercase.Propertyfoo_bar-baz leads to setter methodsetFooBarBaz.

    1. If it has a type hint in the method signature then its type used:

      public function setPerson(Contact $person) {...}
    2. The method's docblock is inspected for@param $type annotations:

      /** * @param Contact $person Main contact for this application */public function setPerson($person) {...}
    3. If no type could be detected, the plain JSON value is passedto the setter method.

  2. Class property types (since PHP 7.4):

    public Contact $person;
  3. Constructor property promotion types (since PHP 8.0):

    public function __construct(protected Contact $person) {}
  4. @var $type docblock annotation of class properties:

    /** * @var \my\application\model\Contact */public $person;

    The property has to be public to be used directly.You may also use$bIgnoreVisibility to utilizeprotected and private properties.

    If no type could be detected, the property gets the plain JSON value set.

    If a property can not be found, JsonMapper tries to find the propertyin a case-insensitive manner.A JSON propertyisempty would then be mapped to a PHP propertyisEmpty.

    Note

    You have to provide the fully qualified namespacefor the type to work. Relative class names are evaluatedin the context of the current classes namespace, NOTrespecting any imports that may be present.

    PHP does not provide the imports via Reflection; the comment text onlycontains the literal text of the type.For performance reasons JsonMapper does not parse the source code on itsown to detect and expand any imports.

  • Simple types

    • string
    • bool,boolean
    • int,integer
    • double,float
    • array
    • object
    • mixed
  • Class names, with and without namespaces

    • Contact - exception will be thrown if the JSON value isnull
  • Arrays of simple types and class names:

    • int[]
    • Contact[]
  • Multidimensional arrays:

    • int[][]
    • TreeDeePixel[][][]
  • ArrayObjects of simple types and class names:

    • ContactList[Contact]
    • NumberList[int]
  • Backed enums, with and without namespaces

    • Suit:string|Suit:int - exception will be thrown if the JSON value is not present in the enum
  • Nullable types:

    • int|null or?int - will benull if the value in JSON isnull, otherwise it will be an integer
    • Contact|null or?Contact - will benull if the value in JSON isnull, otherwise it will be an object of typeContact

ArrayObjects and extending classes are treated as arrays.

Variables without a type or with typemixed will get theJSON value set directly without any conversion.

Seephpdoc's type documentation for more information.

Note

This feature is disabled by default for security reasons since version 5.See$bStrictObjectTypeChecking for details.

When an object shall be created but the JSON contains a simple typeonly (e.g. string, float, boolean), this value is passed tothe classes' constructor. Example:

PHP code:

public DateTime$date;

JSON:

{"date":"2014-05-15"}

This will result innew DateTime('2014-05-15') being called.

When variables are defined as objects of abstract classes or interfaces,JsonMapper would normally try to instantiate those directly and crash.

Using JsonMapper's$classMap property, you can specify which classesshall get instantiated instead:

$jm =newJsonMapper();$jm->classMap['Foo'] ='Bar';$jm->map(...);

This would create objects of typeBar when a variable is defined to beof typeFoo.

It is also possible to use a callable in case the actual implementation classneeds to be determined dynamically (for example in case of a union).The mapped class ('Foo' in the example below) and the Json data are passed asparameters into the call.

$mapper =function ($class,$jvalue) {// examine $class and $jvalue to figure out what class to use...return'DateTime';};$jm =newJsonMapper();$jm->classMap['Foo'] =$mapper;$jm->map(...);

JsonMapper throws an exception when a JSON property isnull,unless the PHP class property has a nullable type - e.g.Contact|null or?Contact.

If your API contains many fields that may benull and you do not wantto make all your type definitions nullable, set:

$jm->bStrictNullTypes =false;

Since version 5.0.0,null values in arrays lead to aJsonMapper_Exceptionunless the type is nullable - e.g.array[?string] orarray[string|null].

To get the previous behavior back (allowing nulls even when not declared so) set:

$jm->bStrictNullTypesInArrays =false;

JsonMapper'ssetLogger() method supports allPSR-3 compatiblelogger instances.

Events that get logged:

  • JSON data contain a key, but the class does not have a propertyor setter method for it.
  • Neither setter nor property can be set from outside because theyare protected or private

During development, APIs often change.To get notified about such changes, JsonMapper can be configured tothrow exceptions in case of either missing or yet unknown data.

When JsonMapper sees properties in the JSON data that arenot defined in the PHP class, you can let it throw an exceptionby setting$bExceptionOnUndefinedProperty:

$jm =newJsonMapper();$jm->bExceptionOnUndefinedProperty =true;$jm->map(...);

You may also choose to handle those properties yourself by settingacallable to$undefinedPropertyHandler:

/** * Handle undefined properties during JsonMapper::map() * * @param object $object    Object that is being filled * @param string $propName  Name of the unknown JSON property * @param mixed  $jsonValue JSON value of the property * * @return void */functionsetUndefinedProperty($object,$propName,$jsonValue){$object->{'UNDEF' .$propName} =$jsonValue;}$jm =newJsonMapper();$jm->undefinedPropertyHandler ='setUndefinedProperty';$jm->map(...);

Or if you would let JsonMapper handle the setter for you, you can return a stringfrom the$undefinedPropertyHandler which will be used as property name.

/** * Handle undefined properties during JsonMapper::map() * * @param object $object    Object that is being filled * @param string $propName  Name of the unknown JSON property * @param mixed  $jsonValue JSON value of the property * * @return void */functionfixPropName($object,$propName,$jsonValue){returnucfirst($propName);}$jm =newJsonMapper();$jm->undefinedPropertyHandler ='fixPropName';$jm->map(...);

Note

This only works when$bStrictObjectTypeChecking stays enabled.

Properties in your PHP classes can be marked as "required" byputting@required in their docblock:

/** * @var string * @required */public$someDatum;

When the JSON data do not contain this property, JsonMapper will throwaJsonMapper_Exception when$bExceptionOnMissingData is activated:

$jm =newJsonMapper();$jm->bExceptionOnMissingData =true;$jm->map(...);

Option$bRemoveUndefinedAttributes causes JsonMapper to remove propertiesfrom the final object if they have not been in the JSON data:

$jm =newJsonMapper();$jm->bRemoveUndefinedAttributes =true;$jm->map(...);

You can allow mapping to private and protected properties andsetter methods by setting$bIgnoreVisibility to true:

$jm =newJsonMapper();$jm->bIgnoreVisibility =true;$jm->map(...);

When a variable's type is a class and JSON data is a simple typelikestring, JsonMapper can pass this value to the class' constructorwhen configured to do so:

$jm =newJsonMapper();$jm->bStrictObjectTypeChecking =false;$jm->map(...);

This can be used to automatically initialize DateTime objectsfrom date strings.

Disabling this strict object type checks may lead to problems, though:

  • When a class does not have a constructor or no constructor parameter,the value will get lost
  • When the constructor has more than 1 required parameter, it will crash.
  • When the constructor's parameter type does not match the one of thedata in JSON, it will crash
  • @required properties will not be filled

Note

The default value changed fromfalse totrue in version 5 toincrease security.

Now you have to opt in if you want to pass simple types tothe class constructor.

You may wish to pass array data intomap() that you got by calling

json_decode($jsonString,true)

By default, JsonMapper will throw an exception becausemap() requiresan object as first parameter.You can circumvent that by setting$bEnforceMapType tofalse:

$jm =newJsonMapper();$jm->bEnforceMapType =false;$jm->map(...);

JsonMapper is able to call a custom method directly on each object aftermapping it is finished:

$jm =newJsonMapper();$jm->postMappingMethod ='afterMapping';$jm->map(...);

NowafterMapping() is called on each mapped object(if the class has that method).

You may pass additional arguments to the post-mapping callback:

$jm =newJsonMapper();$jm->postMappingMethod ='afterMapping';$jm->postMappingMethodArguments = [23,'foo'];$jm->map(...);

Via Composer fromPackagist:

$ composer require netresearch/jsonmapper

Alternatives

JsonMapper is licensed under theOSL 3.0.

JsonMapper follows thePEAR Coding Standards.

Christian Weiske,cweiske.de

About

Map nested JSON structures onto PHP classes

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors34

Languages


[8]ページ先頭

©2009-2025 Movatter.jp