nstwf/json-mapper

Map json data to objects

0.8.4 2022-06-07 05:56 UTC

README

Library for mapping json data to objects using constructor

Downloads Pipeline status Code coverage Tag PHP

Contents

Installation

composer require nstwf/json-mapper

Usage

$factory = new \Nstwf\JsonMapper\JsonMapper\JsonMapperFactory();
$jsonMapper = $factory->create();

$data = (object)[
    "id"      => "123e4567-e89b-12d3-a456-426614174000",
    "roles"   => [1, 3, 7],
    "profile" => [
        "name" => "Alex",
        "age"  => 19,
    ]
];

$jsonMapper->mapObject($data, User::class);

Supported types:

  • Scalar types (int, float, bool, string, array, mixed)
  • Enums
  • Classes
  • Array of scalar types, enums and classes
  • Nullable types

Inspectors

Here describe all supported inspectors. You can customize them, if you do not use some features describe there - to optimize it

Notes:

  • To declare array of any type (especially for class and enum) - use phpDoc
  • If class in phpDoc does not contain namespace - try to discover namespace from the use statement if it exists
  • Constructor parameter must use same name as property
  • Union type is chosen by equal comparison with the data type:
    • For scalar only: if no one of described types is not suitable - value will be cast to first type
  • Union types declare from left to right.
    • Be careful: enum with backed type can be mapped as scalar type value, if it uses together in union. Preferred types should come first

Typed properties

$inspector = (new \Nstwf\JsonMapper\Inspector\InspectorBuilder())
                ->withTypesProperties()
                ->build();
class Example {
    private int $scalar;
    private int|string $union;
    private ?int $nullable;
    private User $class;
}

Property @var annotations

$inspector = (new \Nstwf\JsonMapper\Inspector\InspectorBuilder())
                ->withPropertyAnnotations()
                ->build();
use App\Models\User;

class Example {
    /** @var int */
    private $scalar;
    /** @var int|string */
    private $union;
    /** @var int[] */
    private $array;
    /** @var int|null */
    private $nullable;
    /** @var User */
    private $class;
    /** @var \App\Models\Role */
    private $classWithNamespace;
}

Constructor typed parameters

$inspector = (new \Nstwf\JsonMapper\Inspector\InspectorBuilder())
                ->withConstructorParameters()
                ->build();
class Example {
    public function __construct(
        int $scalar,
        int|string $union,
        ?int $nullable,
        User $class
    ) {
       $this->scalar = $scalar;
       /** more */
    }
}

Constructor @param annotations

$inspector = (new \Nstwf\JsonMapper\Inspector\InspectorBuilder())
                ->withConstructorAnnotations()
                ->build();
use App\Models\User;

class Example {

    /**
    * @param int                $scalar
    * @param int|string         $union
    * @param int[]              $array
    * @param int|null           $nullable
    * @param User               $class
    * @param \App\Models\Role   $classWithNamespace
     */
    public function __construct(/*parameters*/) {}
}

Property CustomName attribute

$inspector = (new \Nstwf\JsonMapper\Inspector\InspectorBuilder())
                ->withCustomNameAttribute()
                ->build();
class Example {
    #[\Nstwf\JsonMapper\Attribute\CustomName('custom_name')]
    private $property;
}

Cache

All inspectors can receive cache. Default cache for inspectors - array. You can use self implementation, using: Psr\SimpleCache\CacheInterface

$inspectorBuilder = new \Nstwf\JsonMapper\Inspector\InspectorBuilder();

$inspector = $inspectorBuilder
               ->withCache($redisCache)
               ->withTypesProperties()
               ->withPropertyAnnotations()
               ->build();

Customize

Use native builders to customize your json mapper for your needs

// Customize inspectors
$inspectorBuilder = new \Nstwf\JsonMapper\Inspector\InspectorBuilder();
$inspector = $inspectorBuilder
               ->withTypesProperties()
               ->withPropertyAnnotations()
               ->build();
                 
// Add own types to registry
$registry = new \Nstwf\JsonMapper\Registry\ClassFactoryRegistry();
$registry->add(Uuid::class, fn(string $value) => new Uuid($value));

// Use object mapper builder
$propertyMapperBuilder = new \Nstwf\JsonMapper\Property\Mapper\PropertyMapperBuilder();
$propertyMapper = $propertyMapperBuilder
                    ->withClassFactoryRegistry($registry)
                    ->build();
                    
$objectMapperBuilder = new \Nstwf\JsonMapper\Object\Mapper\ObjectMapperBuilder();
$objectMapper = $objectMapperBuilder
                    ->withPropertyMapper($propertyMapper)
                    ->build();

// Create JsonMapper
$factory = new \Nstwf\JsonMapper\JsonMapper\JsonMapperFactory();

$jsonMapper = $factory->create($inspector, $objectMapper);

Customize types mapping

You can customize mapping for some classes. For example: json contains uuid as string, but in the object it defined as Uuid class. Just add class with custom mapping

{
  "id": "123e4567-e89b-12d3-a456-426614174000"
}
class User {
    private Uuid $id;
}
$registry = new \Nstwf\JsonMapper\Registry\ClassFactoryRegistry();

$registry->add(
    Uuid::class, fn(string $value) => new Uuid($value)
);

Customize inspectors

You can specify type discover using builder. All available inspectors defined here.

// Example: if you do not use constructors
$builder = new \Nstwf\JsonMapper\Inspector\InspectorBuilder();
$builder->withTypesProperties()
        ->withPropertyAnnotations()
        ->build();

Based on: