Coder Social home page Coder Social logo

jsonmapper / jsonmapper Goto Github PK

View Code? Open in Web Editor NEW
204.0 7.0 26.0 494 KB

Map nested JSON structures to PHP classes

Home Page: https://jsonmapper.net

License: MIT License

PHP 100.00%
mapping hydration json php jsonmapper middleware mapper hacktoberfest

jsonmapper's Introduction

Logo

JsonMapper is a PHP library that allows you to map a JSON response to your PHP objects that are either annotated using doc blocks or use typed properties. For more information see the project website: https://jsonmapper.net/

GitHub Packagist Version Packagist Downloads PHP from Packagist GitHub Actions Workflow Status Coverage Status

Why use JsonMapper

Continuously mapping your JSON responses to your own objects becomes tedious and is error-prone. Not mentioning the tests that needs to be written for said mapping.

JsonMapper has been build with the most common usages in mind. In order to allow for those edge cases which are not supported by default, it can easily be extended as its core has been designed using middleware.

JsonMapper supports the following features

  • Case conversion
  • Debugging
  • DocBlock annotations
  • Final callback
  • Namespace resolving
  • PHP 7.4 Types properties

Installing JsonMapper

The installation of JsonMapper can easily be done with Composer

$ composer require json-mapper/json-mapper

The example shown above assumes that composer is on your $PATH.

How do I use JsonMapper

Given the following class definition

namespace JsonMapper\Tests\Implementation;

class SimpleObject
{
    /** @var string */
    private $name;

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}

Combined with the following JsonMapper code as part of your application

$mapper = (new \JsonMapper\JsonMapperFactory())->default();
$object = new \JsonMapper\Tests\Implementation\SimpleObject();

$mapper->mapObject(json_decode('{ "name": "John Doe" }'), $object);

var_dump($object);

The above example will output:

class JsonMapper\Tests\Implementation\SimpleObject#1 (1) {
  private $name =>
  string(8) "John Doe"
}

Customizing JsonMapper

Writing your own middleware has been made as easy as possible with an AbstractMiddleware that can be extended with the functionality you need for your project.

use JsonMapper;

$mapper = (new JsonMapper\JsonMapperFactory())->bestFit();
$mapper->push(new class extends JsonMapper\Middleware\AbstractMiddleware {
    public function handle(
        \stdClass $json,
        JsonMapper\Wrapper\ObjectWrapper $object,
        JsonMapper\ValueObjects\PropertyMap $map,
        JsonMapper\JsonMapperInterface $mapper
    ): void {
        /* Custom logic here */
    }
});

Contributing

Please refer to CONTRIBUTING.md for information on how to contribute to JsonMapper.

List of Contributors

Thanks to everyone who has contributed to JsonMapper! You can find a detailed list of contributors of JsonMapper on GitHub.

Sponsoring

JetBrains

This project is sponsored by JetBrains providing an open source license to continue building on JsonMapper without cost.

License

The MIT License (MIT). Please see License File for more information.

jsonmapper's People

Contributors

aagjalpankaj avatar creimer avatar dahse89 avatar dannyvdsluijs avatar dependabot-preview[bot] avatar dependabot[bot] avatar guillaumesmo avatar lambstream avatar paresh27 avatar tachigami avatar tiso avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonmapper's Issues

Add unit tests that only test a single unit

The current test have outgrown its purpose as it was mainly used for me personally to keep track of the impact on the envisioned scenarios.

A separate folder called unit should contain all the unit tests and the current test should be moved to a folder called integration.

Add (multiple) middleware that apply case conversion on the JSON object

With different several formats between different programming languages it would be helpful to offer case conversion as middleware that can be pushed to the stack.

As there is no recommended case format (not even in PSR-1, see https://www.php-fig.org/psr/psr-1/#42-properties) targets should be: $StudlyCaps, $camelCase, or $under_score. Source could be any of the targets but also $kebab-case. Also consider more subtle differences such as a leading uppercase.

Ideally the source and target are only settings of a single middleware called CaseConversionMiddleware

Reference:
cweiske/jsonmapper#149
cweiske/jsonmapper#152

Property mapper does an optimistic merge, loosing details along the way

Given a property has been defined as:

    /** @var null|array<ModelB> */
    public ?array $models;

It will fail the mapping with the typed properties middleware being executed after the docblock one. And the property models will be considered as an array of mixed types.

To Reproduce
See below unit test that will fail

<?php
use JsonMapper\JsonMapperFactory;
use PHPUnit\Framework\TestCase;
class ModelB
{
    public string $name;
}
class ModelA
{
    public string $name;
    /** @var null|array<ModelB> */
    public ?array $models;
}
/**
 * @coversNothing
 */
class Test extends TestCase
{
    private const JSON = <<<'JSON'
[{
	"name": "This is ModelA",
	"models": [{
		"name": "This is ModelB"
	}]
}]
JSON;
    public function testMapping(): void
    {
        $json = json_decode(self::JSON)[0];
        $result = new ModelA();
        $mapper = (new JsonMapperFactory())->bestFit();
        $mapper->mapObject($json, $result);
        self::assertInstanceOf(ModelA::class, $result);
        self::assertEquals($result->name, 'This is ModelA');
        self::assertInstanceOf(ModelB::class, $result->models[0]);
        self::assertEquals($result->models[0], 'This is ModelB');
    }
}

Expected behavior
The test should pass and any information into the property map should be truely merged or even discarded when not adding more details.

Stacktrace
The above test will fail Failed asserting that stdClass Object (...) is an instance of class "ModelB".

Environment (please complete the following information):

  • PHP: 7.4.11
  • JsonMapper: 2.2.0

Add more middleware

As I want JsonMapper to support as many use case as possible I invite you to add a new middleware covering a use case we have missed. Due to the use of middleware even less common use cases can easily be added.

Travis builds no longer work due to new payment model.

Describe the bug
With the new approach of Travis in their payment model builds are no longer running. We should complete the step to coverage from the GitHub actions.

To Reproduce
Steps to reproduce the behavior:
When running builds on Travis they don’t complete as there ar w no credits available.

Expected behavior
The build should be complete running on GitHub actions including coverage.

Stacktrace
N/A

Environment (please complete the following information):

  • Travis-ci.com

Additional context
N/A

Todo:

  • Remove Travis config
  • Replace build status badge
  • Update mergify config to look at GitHub Actions results
  • Add coveralls upload to GitHub Actions workflow
  • Update protected branches config to no longer require travis but require GitHub Actions

Improve caching

Hi!

I might did it wrong way but this drastically improves parsing speed on my system (from 16s to 6s on 36MB json file with nested objects)!!!

$cache = new ArrayCache();
$mapper = (new JsonMapperFactory())->create(
    new PropertyMapper(),
    new DocBlockAnnotations($cache),
    new TypedProperties($cache),
    new NamespaceResolver($cache)

);

than this (new JsonMapperFactory())->bestFit()

It is slow because of individual cache object is used per middleware instead of shared one.

public function withDocBlockAnnotationsMiddleware(?CacheInterface $cache = null): JsonMapperBuilder
{
return $this->withMiddleware(new DocBlockAnnotations($cache ?: clone $this->defaultCache), DocBlockAnnotations::class);
}
public function withNamespaceResolverMiddleware(?CacheInterface $cache = null): JsonMapperBuilder
{
return $this->withMiddleware(new NamespaceResolver($cache ?: clone $this->defaultCache), NamespaceResolver::class);
}
public function withTypedPropertiesMiddleware(?CacheInterface $cache = null): JsonMapperBuilder
{
return $this->withMiddleware(new TypedProperties($cache ?: clone $this->defaultCache), TypedProperties::class);
}

May it be better to use a shared cache?

Add support for mixed type introduced with PHP 8

Is your feature request related to a problem? Please describe.
With the introduction of PHP 8 JsonMapper should support the mixed type to support all active PHP versions.

Describe the solution you'd like
The parsing of doc block and properties should support the mixed return type which means the value is set as is from the json. No type casting should be applied. This has the downside that any implementation using the mixed type will not be type safe, this can be a implementation detail which should not affect JsonMapper

Describe alternatives you've considered
None, as the mixed type is a native feature of PHP 8 (and has been PHP < 8 using doc blocks)

Additional context
https://wiki.php.net/rfc/mixed_type_v2

NamespaceResolver does not correctly lookup namespace for properties in parent class

Describe the bug
I want to map a class which extends another class and has property with phpdoc (class).

<?php
declare(strict_types = 1);

namespace Foo

use Foo\Model\BarModel

class test extends BarModel
{
}
<?php
declare(strict_types = 1);

namespace Foo\Model

use Foo\MetaModel\Meta;

class BarModel
{
    /** @var null|Meta */
    public $meta;
}
<?php

declare(strict_types = 1);

namespace Foo\MetaModel;

class Meta
{
    /** @var null|string */
    public $type;
}

The Namespaceresolver does not correct lookup the namespace of the used Foo\MetaModel\Meta.
Instead it will look only for Meta. Only a complete phpdoc helps here with Foo\MetaModel\Meta

PHP Version: 7.2
Json-Mapper: 2.9.0
Mode: (new JsonMapperFactory())->bestFit()

Cannot lookup property of namespace in parent class

Describe the bug
The mapper does not correctly lookup the namespace of a property in a parent class correctly

To Reproduce
I have something like these classes:

<?php

declare(strict_types = 1);

namespace Foo;

use Bar\UpdateCustomer;

class Customer extends UpdateCustomer
{
}
<?php

declare(strict_types = 1);

namespace Bar;

class UpdateCustomer
{
    /** @var null|CustomerState */
    public $customerState;

}
<?php

declare(strict_types = 1);

namespace Bar;

class CustomerState
{
    /** @var null|string */
    public $description;
}

Expected behavior
The properties should be mapped correctly

Stacktrace
Unable to map to CustomerState

Environment (please complete the following information):

  • PHP: 7.2

Additional context

It seems the Bar namespace is not evaluated with the lookup for the CustomerState.
If I just a "use Bar\CustomerState;" to the initial customer class it works.

Different json key and PHP variable naming

I have a json structure that has a property called categories[]. The problem is that this thing cannot be mapped, because it contains special characters in the name, so I cannot declare $categories[] as variable name in PHP.

Having a possibility to add manual mapping description would be very useful. I can imagine this being used in many various scenarios where developers do not want the json key and the PHP variable having the exact same name.

Allow to map directly from JSON string

As it is quite common to have a JSON in string format. It would be helpful for users of the library to map from the string instead of an object. This would reduce their boiler plate code when using JsonMapper together with an HTTP JSON API.

The desired solution should add some convenience methods such as mapObjectFromString and mapArrayFromString to the JsonMapperInterface and it's implementation.

JsonMapper does not lookup Class Name correctly in array Mapping

Describe the bug
I have a attributecollection and inside a class variable $items with Attribute[].
The json mapper should map the attribute with the same namespace, but instead lookup for another Attribute class.
The only workaround is to add the full namespace in the phpdoc.

To Reproduce

<?php

declare(strict_types = 1);

namespace Client\Attributes;

class AttributeCollection
{
    /** @var \Client\Attribute[] */
    public array $items = [];
}
<?php

declare(strict_types = 1);

namespace Client\Attributes;

class Attribute
{
    public string $key;
}

It works without the phpdoc @var attribute and full phpdoc " /** @var \Client\Attribute[] /"
but not with: /
* @var Attribute[] */ (The Collection and the attribute are in the same namespace)
I think the json mapper does not look for a class in the same namespace before looking globally.

Making own middleware - a bit unclear how to do so…

It is possible to figure out how to make your own middleware looking at existing ones.

But it is not clear why AbstractMiddleware is used as a base class in some cases and in others I can see interfaces are used like MiddlewareInterface and MiddlewareLogicInterface.

Middleware "Base"
AbstractMiddleware MiddlewareInterface, MiddlewareLogicInterface
CaseConversion AbstractMiddleware
Debugger AbstractMiddleware
DocBlockAnnotations AbstractMiddleware
FinalCallback MiddlewareInterface
NamespaceResolver AbstractMiddleware
TypedProperties AbstractMiddleware
AbstractMiddleware AbstractMiddleware

Maybe if there was a dedicated docs page on how to start on building your own middleware it would help?
Just a few pointers as a starter, e.g.

  • to get started writing your own middleware, use xyz
  • for more details, look into existing middleware and e.g. link to source code

PropertyMapper cannot map with array in phpdoc

Describe the bug
I have a property like this:
php
/** @var string|string[] */
public $value;
php

The PropertyMapper in line 160 (v. 2.9.1) uses only the first match (in this case string) for scalar mapping.
If I have a valid empty array the PropertyMapper in line 225 calls the scalarcaster and throws an error.

I use "(new JsonMapperFactory())->bestFit()" with PHP 7.4

Ensure that the JSON object is complete

Describe the bug
JsonMapper does not throw an exception if the source JSON is incomplete and the property in the target class is not marked as nullable. Accessing the property afterwards ends in a PHP fatal error "Uncaught Error: Typed property UserGroup::$name must not be accessed before initialization"

To Reproduce

class UserGroup {
    public ?int $id = null;
    public string $name;
    /** @var array<int> */
    public array $permissions = [];
}
{
 "id": null
}

Expected behavior
Some kind of exception tells me that the source JSON is incomplete

Environment (please complete the following information):

  • PHP: 8.1.7

Mapping to classes from the same namespace and

Describe the bug
When mapping data to an class which has an class from the same namespace it is applied twice.

To Reproduce
Having the following code and see the error thrown

<?php

declare(strict_types = 1);

namespace App\Data\User\Model;

class User
{
    public int $id;
    public UserRole $role;
}
<?php

declare(strict_types = 1);

namespace App\Data\User\Model;

class UserRole
{
    public string $name;
}
$factory = new \JsonMapper\JsonMapperFactory();
$mapper = $factory->bestFit();

$jsonObject = (object) [
  'id' => 42, 
  'role' => (object) ['name' => 'root']
];
$user = new \App\Data\User\Model\User();

$mapper->mapObject($jsonObject, $user);
�PHP Error:  Class 'App/Data/User/Model/App/Data/User/Model/UserRole' not found in /Users/dannyvandersluijs/Projects/stackoverflow-laravel-playground/vendor/json-mapper/json-mapper/src/Handler/PropertyMapper.php on line 63�

Expected behavior
It is expected to have a valid User object with a User role.

Stacktrace
If applicable, add the stack trace to help explain your problem.

Environment (please complete the following information):
Running PHP 7.4.8

Additional context
Reported by Radim using email.

JsonMapper::mapObject should returned the mapped object

Is your feature request related to a problem? Please describe.
With the addition of Psalm annotations we have some form of generics (at least with regards to the auto completion.
It would be helpful to return the mapped object as other JSON mapping packages do so. This would make it a better drop-in replacement.

Describe the solution you'd like
Return the mapped object from mapObject and mapObjectFromString and use Psalm notations to ensure the IDE auto completion provides some valuable feedback.

Describe alternatives you've considered
None, leaving it as is could be fine but makes it less easier to provide JsonMapper as a drop-in replacement.

Add support to call a method after the mapping is completed

In order to call a method at the end of mapping a simple middleware could be added that can be constructed with a callback. This will enable users to easily decorate their existing software as middleware for JsonMapper.

Consider the call back being invoked at each mapping (recursive) or only when leaving the top-level mapping method.

Reference: cweiske/jsonmapper#142

Allow PropertyMapper and all Middlewares to return the object

Is your feature request related to a problem? Please describe.
In order to enable object construction on a top level the result needs to be able to be passed back.

Describe the solution you'd like
And methods in the PropertyMapper and Middleware need to return the result. Psalm annotations should be used to indicate what t type is being returned.

Describe alternatives you've considered
None.

Additional context
This is part of the JsonMapper 3.0 milestone.

Support PHP 8.1 Enum

Is your feature request related to a problem? Please describe.
With the release of PHP 8.1 being later this year support for enums is added to PHP.

Describe the solution you'd like
JsonMapper should support Enum when mapping Json to PHP objects.

Describe alternatives you've considered
The alternative of not supporting doesn't fit the goal of JsonMapper to support the common PHP features.

Additional context
https://wiki.php.net/rfc/enumerations

Try and support PHP 7.1

Is your feature request related to a problem? Please describe.
In order to use JsonMapper in a version of Psalm, PHP7.1 support is needed.

Describe the solution you'd like
Ideally we should support older PHP versions in order to increase the number of potentials packages that can use this library.

Describe alternatives you've considered
None, lets just see if we can support PHP 7.1 without too many effort.

Additional context
None

PHP 8.0 is allowed to fail

Describe the bug
In the build chain PHP 8.0 (Nightly) is still allowed to fail.

To Reproduce
Steps to reproduce the behavior:
See the build report where Nightly is allowed to fail.

Expected behavior
A breaking test in PHP 8.0 should fail the build. The Nightly (target PHP 8.1) is still allowed to fail.

Stacktrace
N/A

Environment (please complete the following information):

  • PHP: 8.0

Additional context
N/A

Add a (shared?) caching layer that solves any redundant calls to find the correct property mapping

Currently for each object Reflection and AST is being used to find the property mapping. For more complex JSON responses or an array mapping it would be best if we can cache the results and reuse them second time round.

Ideally this cache should be memory based only. When doing persisted caching there might be a risk of invalid caching when changing properties which are no longer correct in cache.

Support variadic setter methods.

Is your feature request related to a problem? Please describe.
As requested here support for variadic setter methods should be added to this version of JsonMapper.

Describe the solution you'd like
Support for these type of setter methods are needed and the property mapper should be able to utilize such a setter.

Describe alternatives you've considered
Since JsonMapper tries to move closer to people’s models instead models moving towards JsonMapper this seems like the correct way forward.

Additional context
N/A

Improve speed by caching result of middleware chain if possible

Is your feature request related to a problem? Please describe.
JsonMapper has some time penalty due the the use of the middleware chain. This becomes more clear when trying to map large sets of data where the result of the middleware chain is the same for each iteration for the same class (depending on the middleware used as some middleware alter the json and other only build a property map)

Describe the solution you'd like
The middleware chain should be cached when possible to avoid the time penalty when mapping a large number of objects

Describe alternatives you've considered
At the time of writing no alternatives are considered other than not improving on the time penalty.

Additional context
This time penalty was made clear by @sergix44 who might be interested in the progress.

Support objects with parameter(s) passes through the constructor.

Is your feature request related to a problem? Please describe.
In its current state I cannot use objects that take their properties through a constructor.

Describe the solution you'd like
JsonMapper should include some support to register how objects should be created to allow for objects not known to the library up front.

Describe alternatives you've considered
As JsonMapper should not prescribe how others should design their code. Therefore no alternative was considered.

Additional context
This feedback was provided by Kit_seals on reddit r/php

Add debug middleware

Currently there is no easy way to debug the results of some middleware

A new debugger middleware that takes an PSR-3 compatible logger and is able to dump the state of the JSON object and/or the state of the property map would be very helpful during development processes.

Up until now I've been using the unit tests and breakpoints during the debug runs to see the states.

Add support for constructor resolving

Is your feature request related to a problem? Please describe.
JsonMapper could benefit from mapping objects with a constructor that requires parameters. If these map one to one with the json provided we should be able to create a fresh object

Describe the solution you'd like
As part of the solution we could use reflection to find the correct constructor call and return the fresh instance with the provided parameters. This should be an ultimate fallback as there will be some time penalty for using reflection. Persisting the results in cache would be helpful to only have the penalty once.
This would also help with an immutable state of the mapped objects

Describe alternatives you've considered
We already have support for object factories in place which can help you with but require you to write the mapping code which in the case of a direct one-to-one mapping becomes tedious.

Additional context
From looking into how other JSON mapping packages work we seem to be lacking this feature.

Support mapping interfaces and abstract classes

In its current state JsonMapper requires exact and concrete matches when trying to map object. E.g. mapping to an abstract class or interface is currently not possible. Secondly having an API that responds with two or more different objects (e.g. a Square or a Circle which both are a Shape) cannot be properly mapped.

In order to support interfaces and abstract classes the mapper should be able to handle those while trying to map the JSON. Specific input from the programmer is needed to match an interface or abstract class to a concrete class. This can be achieved similar to how a Dependency Injection container works.
Currently JsonMapper already supports factories for custom object creation this might be a good way in in order to achieve this feature.

Alternatively this feature could be discarded but having this would be a nice addition to the library and become beneficial for future projects which embrace the type safe approach the PHP language is currently taking.

An example of a failing test (fails with Error : Cannot instantiate abstract class JsonMapper\Tests\Integration\Shape) is:

<?php

declare(strict_types=1);

namespace JsonMapper\Tests\Integration;

use JsonMapper\JsonMapperFactory;
use PHPUnit\Framework\TestCase;

class InheritanceTest extends TestCase
{
    public function testCanMapToAnAbstractClass(): void
    {
        $mapper = (new JsonMapperFactory())->bestFit();
        $response = new Response();
        $mapper->mapObjectFromString(
            '{"shape": {"length": 4, "width": 4}}',
            $response
        );

        self::assertEquals(16, $response->shape->getCircumference());

    }
}

class Response {
    /** @var Shape */
    public $shape;
}

abstract class Shape {
    abstract public function getCircumference(): float;

    public function getType(): string
    {
        return get_class();
    }
}

class Square extends Shape {
    /** @var int */
    private $width;
    /** @var int */
    private $length;

    public function __construct(int $width, int $length)
    {
        $this->width = $width;
        $this->length = $length;
    }


    public function getCircumference(): float
    {
        return ($this->length * 2) + ($this->width * 2);
    }
}

Can’t remove middleware from the stack

Is your feature request related to a problem? Please describe.
Currently it isn’t possible to remove a middleware from the stack even though it is added by a name.
Describe the solution you'd like
A new method should be added to remove middleware from the stack by its name. This should also clear the cached stack as it has been altered. Also consider if an exception is needed when removing something from the stack when not available or if it is allowed to fail silently.

Additional context
When using PHP 7.4 and only typed properties you don’t want to have the doc block middleware

Unable to map data where a property is of the type \stdClass (or any other builtin class for that matter)

Describe the bug
When trying to map data to a class which somewhere in it's property tree contains an \stdClass' throws an exception RuntimeException : Class stdClass has no filename available`

To Reproduce
A integration test causing the described exception:

public function testItCanMapCustomClassWithStdClassProperty(): void
    {
        // Arrange
        $mapper = (new JsonMapperFactory())->bestFit();
        $response = new class {
            public \stdClass $properties;
        };
        $json = (object) ['properties' => (object) ['one' => 1, 'two' => 2]];

        // Act
        $mapper->mapObject($json, $response);

        // Assert
        self::assertEquals((object) ['one' => 1, 'two' => 2], $response->properties);
    }

Expected behavior
I would expect the \stdClass property be filled/populated based on the JSON inputs

Stacktrace

Runtime:       PHP 7.4.14 with Xdebug 3.0.3
Configuration: /Users/danny/Projects/OSS/JsonMapper/JsonMapper/phpunit.xml.dist


RuntimeException : Class stdClass has no filename available
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Helpers/UseStatementHelper.php:19
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Middleware/NamespaceResolver.php:47
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Middleware/NamespaceResolver.php:34
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Middleware/AbstractMiddleware.php:23
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Middleware/AbstractMiddleware.php:25
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Middleware/AbstractMiddleware.php:25
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/JsonMapper.php:97
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Handler/PropertyMapper.php:230
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Handler/PropertyMapper.php:175
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Handler/PropertyMapper.php:55
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Middleware/AbstractMiddleware.php:25
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Middleware/AbstractMiddleware.php:25
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/Middleware/AbstractMiddleware.php:25
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/src/JsonMapper.php:97
 /Users/danny/Projects/OSS/JsonMapper/JsonMapper/tests/Integration/JsonMapperTest.php:537

Environment (please complete the following information):

  • PHP: 7.4.14 (At least tested on, expect all PHP Version to be affected
  • JsonMapper: 2.4.1

Additional context
None

Do not automatically cast scalar types in "strict" mode

It should be possible to throw an exception with incorrect scalar type casting.

Currently the scalar type casting is automatically depending on typed properties|doc block

public int $counter;

If the json returns a string, and not a number an exception should/could be thrown

I like a strict type mapping and not an automatically casting of the values.
For Implementing a REST API and json mapping it is easier to see a possible response problem at once (false return type).
A Feature with a strict mode casting would be nice.

NamespaceResolver doesn't cache anything resulting in imports being fetched on every run

Describe the bug
The first loc that gets executed on the NamespaceResolver is:

$imports = UseStatementHelper::getImports($object->getReflectedObject());

without caching or any other optimisation. This means the umber of times an object is mapped is equal the the number
of times the imports are loaded. Even when mapping the same object. The UseStatementHelper::getImports is an expensive call as it loads the object class source code into an AST to fetch the used imports.

To Reproduce
I've created a vanilla script

<?php
# ./tests/Benchmark/vanilla.php

declare(strict_types=1);

use JsonMapper\JsonMapperFactory;
use JsonMapper\Tests\Benchmark\Joke;

chdir(__DIR__ . '/../../');
require_once 'vendor/autoload.php';

$mapper = (new JsonMapperFactory())->bestFit();

for ($x = 1; $x < 1000; $x++) {
    $joke = new Joke();
    $json = '{"id":131,"type":"general","setup":"How do you organize a space party?","punchline":"You planet."}';
    $mapper->mapObjectFromString($json, $joke);
}

which I've executed with the Xdebug profile enabled. See the screenshot attached. It shows that getting the imports took 80%time.
Screenshot 2021-04-15 at 21 01 20

Expected behavior
Since the source code is not expected to change at runtime we should cache the results similar how the other middleware does this.

Stacktrace

Environment (please complete the following information):

  • PHP: 8.0
  • XDebug: 3.0

Additional context
This research was triggered by the question from @TeroBlaZe in #80

Migrate CI to Github Actions

Let's migrate from Travis to Github to reduce on additional services. Ideally the same matrix and steps from Travis are migrated to Github Actions.

The TypedProperties middleware incorrectly marks private array $friends as nullable.

Describe the bug
The TypedProperties middleware incorrectly marks private array $friends as nullable.

To Reproduce
While running the following code through the types properties middleware the friends property is marked as nullable. When trying to access the property from a new instance an error is thrown: Typed property JsonMapper\Tests\Implementation\Php74\Popo::$friends must not be accessed before initialization indicating this isn't allowed null.

<?php

declare(strict_types=1);

namespace JsonMapper\Tests\Implementation\Php74;

class Popo
{
    public string $name;

    public array $friends;
}

Expected behavior
The friends property (which is an array of mixed) should not be nullable as there is no explicit null mark (?).

Question: How to map dynamic properties?

I want to build a client for the partnerize brands api: (campaigns endpoint here)
https://api-docs.partnerize.com/brand/#tag/Campaigns

The response of the "campaign_languages" can in real be not existing, emtpy string, null or an object of key values like:

                "campaign_languages": {
                    "en_us": {
                        "destination_url": "https:\/\/www.foo.com
                    }
                }

My phpdoc looks like this:

    /** @var null|string|string[] */
    public $campaign_languages;

My Problem is that the key "en_us" and "destination_url" are dynamic and can be "de_de" "source_url" or whatever.
Do you have any ideas how to map this into a object structure or dynamic?

Issue parsing nullable arrays on Docblocks

First of all, thank you very much for maintaining this and releasing the last version that quick.
There is just another tricky type that doesn't seem to be correctly parsed, nullable arrays.

DocBlocks like this one raise an exception as are not considered nullable arrays, but the whole string[] gets taken as the type:

/**
 * string[]|null
 */
$myVAr

I've created some failing tests in this commit, so its easier to debug.
Again, thank you for your support!

Add support for native union type introduced with PHP 8

Is your feature request related to a problem? Please describe.
In order to fully support any PHP8 code base JsonMapper should be able to deal with the new union types rfc

Describe the solution you'd like
The JsonMapper internals should be able to coop with any union types as specified in the RFC linked above.
Some consideration should go into which would be the valid type when the JSON value doesn't match any of the types provided in the union. Would the first provided type (if order is kept) be the logical one to pick or would some magic parsing be better with so priority of types?

Describe alternatives you've considered
None, as this is core functionality which should be part of JsonMapper in order to support all actively supported PHP versions.

Additional context
In addition adding union support to the DocBlock middleware should also be relatively easy.

Nullable property DocBlocks throwing an exception

While serializing objects I'm getting some properties set to null sometimes, and this is crashing my application with the following message:

Argument 1 passed to JsonMapper\JsonMapper::mapObject() must be an instance of stdClass, null given

As json-mapper/json-mapper/src/JsonMapper.php::mapObject gets the null value instead of the json object corresponding to the standard response.

I've tried setting the variable as nullable in the docblock but it seems the whole comment line is taken as a string instead of separated in components.
When I update this:

/**
 * @var TravelperkBankAccount
 */
public $travelperkBankAccount;

To this:

/**
 * @var TravelperkBankAccount|null
 */
public $travelperkBankAccount;

I get this:

Class 'Namelivia\TravelPerk\Expenses\Types\TravelperkBankAccount|null' not found

on json-mapper/json-mapper/src/Handler/PropertyMapper.php::mapPropertyValue.

To my understanding what comes on the type comment is not being parsed and broken down into components, because I get a similar error too if I write:

/**
 * @var TravelperkBankAccount This is a bank account number.
 */
public $travelperkBankAccount;

More or less I think how could this new feature would be implemented, so if you confirm my assumptions are right I can try implementing it myself and opening a PR.

Add a middleware to support Psalm annotations also known as PHP generics

Is your feature request related to a problem? Please describe.
Given the psalm annotations are starting become more and more common it should be possible for JsonMapper to support those using a new middleware so it only needs to be included explicitly enabled by a developer. I've seen some request in other mapper libraries e.g. cweiske/jsonmapper#165

Describe the solution you'd like
Psalm knows many annotations but to get started supporting the common annotated arrays would be great.

Describe alternatives you've considered
We could skip the feature all together. But I prefer a feature rich library with functionality you can with on and off with the flip of a switch.

Additional context
Should be able to deal with the following example:

class Zoo
{
    /** @var array<Animal> */
    public $animals;
}

Getting started:

  • Create a new middleware like DocBlockAnnotations
  • Parse the doc blocks using the know Psalm formats
  • Add unit tests
  • Add integration tests

Case conversion middleware removes original key when replacement key is same

Describe the bug
Case conversion middleware removes original key when replacement key is same

To Reproduce
Steps to reproduce the behaviour:
With the code below you can see there is no name, description or trading attribute

$response = Http::get('https://www.bitstamp.net/api/v2/trading-pairs-info/')->object();

$mapper = (new \JsonMapper\JsonMapperFactory())->bestFit();
$mapper->push(new \JsonMapper\Middleware\CaseConversion(
    \JsonMapper\Enums\TextNotation::UNDERSCORE(),
    \JsonMapper\Enums\TextNotation::CAMEL_CASE()
));
$logger = new \Psr\Log\Test\TestLogger();
$mapper->push(new \JsonMapper\Middleware\Debugger($logger));

$tradingPair = new \App\ValueObject\TradingPair();
$mapper->mapObject($response[0], $tradingPair);

var_dump($response[0]);
var_dump($logger->records[0]['context']['json']);

Expected behaviour
Expectation is to have attributes for which the replacement key is the same remain available.

Badges for License, PHP version and packages are wrong

Describe the bug
Badges for License, PHP version and packages are wrong. All of them link to the the badge image instead of something logical or no link at all.

To Reproduce

  1. Click on the mentioned badges on https://github.com/JsonMapper/JsonMapper and see the badge image being opened in a new tab.

Expected behavior
The packages badge should open https://packagist.org/packages/json-mapper/json-mapper. The license badge should open https://choosealicense.com/licenses/mit/ and the PHP badge can be without a link.

Environment (please complete the following information):
N/A

Additional context
N/A

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.