Pega dados recuperados de um serviço web JSON e os converte em objetos e matrizes aninhados - usando suas próprias classes de modelo.
Começando a partir de um objeto base, ele mapeia dados JSON nas propriedades da classe, convertendo-os nos tipos ou objetos simples corretos.
É um pouco como o mapeamento de parâmetros SOAP nativos que SoapClient
do PHP oferece, mas para JSON. Ele não depende de nenhum esquema, apenas das definições de classe PHP.
A detecção de tipo funciona analisando declarações de tipo e anotações @var
docblock de propriedades de classe, bem como dicas de tipo em métodos setter.
Você não precisa modificar suas classes de modelo adicionando código específico JSON; ele funciona automaticamente analisando docblocks já existentes.
Esta biblioteca não possui dependências.
Palavras-chave: desserialização, hidratação
Conteúdo
map()
As classes de modelo precisam ser escritas à mão
Como o JsonMapper não depende de nenhuma informação de esquema (por exemplo, do json-schema), as classes de modelo não podem ser geradas automaticamente.
netresearch/jsonmapper
com compositorJsonMapper
map
ou mapArray
, dependendo dos seus dadosMapeie um objeto normal:
<?php
require ' autoload.php ' ;
$ mapper = new JsonMapper ();
$ contactObject = $ mapper -> map ( $ jsonContact , new Contact ());
// or as classname
$ contactObject = $ mapper -> map ( $ jsonContact , Contact::class);
Mapeie uma matriz de objetos:
<?php
require ' autoload.php ' ;
$ mapper = new JsonMapper ();
$ contactsArray = $ mapper -> mapArray (
$ jsonContacts , array (), ' Contact '
);
Em vez de array()
você também pode usar ArrayObject
e classes derivadas, bem como classes que implementam ArrayAccess
.
JSON de um serviço web de catálogo de endereços:
{
"name" : "Sheldon Cooper" ,
"address" : {
"street" : "2311 N. Los Robles Avenue" ,
"city" : "Pasadena"
}
}
Sua classe Contact
local:
<?php
class Contact
{
/**
* Full name
*/
public string $ name ;
public ? Address $ address ;
}
Sua classe Address
local:
<?php
class Address
{
public $ street ;
public $ city ;
public function getGeoCoords ()
{
//do something with $street and $city
}
}
O código do seu aplicativo:
<?php
$ json = json_decode ( file_get_contents ( ' http://example.org/sheldon.json ' ));
$ mapper = new JsonMapper ();
$ contact = $ mapper -> map ( $ json , new Contact ());
echo " Geo coordinates for " . $ contact -> name . " : "
. var_export ( $ contact -> address -> getGeoCoords (), true );
JsonMapper
usa diversas fontes para detectar o tipo correto de uma propriedade na seguinte ordem:
Método setter ( set
+ ucwords($propertyname)
)
Sublinhados " _
" e hífens " -
" tornam a próxima letra maiúscula. A propriedade foo_bar-baz
leva ao método setter setFooBarBaz
.
Se houver uma dica de tipo na assinatura do método, seu tipo será usado:
função pública setPerson(Contato $pessoa) {...}
O docblock do método é inspecionado para anotações @param $type
:
/** * @param Contato $person Contato principal deste aplicativo */ função pública setPerson($pessoa) {...}
Se nenhum tipo puder ser detectado, o valor JSON simples será passado para o método setter.
Tipos de propriedades de classe (desde PHP 7.4):
Contato público $pessoa;
Tipos de promoção de propriedade de construtor (desde PHP 8.0):
função pública __construct(Contato protegido $pessoa) {}
@var $type
anotação docblock das propriedades da classe:
/** * @var meuaplicativomodeloContato */ público $pessoa;
A propriedade deve ser pública para ser usada diretamente. Você também pode usar $bIgnoreVisibility para utilizar propriedades protegidas e privadas.
Se nenhum tipo puder ser detectado, a propriedade obterá o conjunto de valores JSON simples.
Se uma propriedade não puder ser encontrada, o JsonMapper tentará encontrá-la sem diferenciar maiúsculas de minúsculas. Uma propriedade JSON isempty
seria então mapeada para uma propriedade PHP isEmpty
.
Observação
Você deve fornecer o namespace totalmente qualificado para que o tipo funcione. Os nomes relativos das classes são avaliados no contexto do namespace da classe atual, NÃO respeitando quaisquer importações que possam estar presentes.
O PHP não fornece as importações via Reflection; o texto do comentário contém apenas o texto literal do tipo. Por motivos de desempenho, o JsonMapper não analisa o código-fonte por conta própria para detectar e expandir quaisquer importações.
Tipos simples
string
bool
, boolean
int
, integer
double
, float
array
object
mixed
Nomes de classes, com e sem namespaces
Contact
– exceção será lançada se o valor JSON for null
Matrizes de tipos simples e nomes de classes:
int[]
Contact[]
Matrizes multidimensionais:
int[][]
TreeDeePixel[][][]
ArrayObjects de tipos simples e nomes de classes:
ContactList[Contact]
NumberList[int]
Enums apoiados, com e sem namespaces
Suit:string|Suit:int
- exceção será lançada se o valor JSON não estiver presente no enum
Tipos anuláveis:
int|null
ou ?int
- será null
se o valor em JSON for null
, caso contrário será um inteiroContact|null
ou ?Contact
- será null
se o valor em JSON for null
, caso contrário será um objeto do tipo Contact
ArrayObjects e classes de extensão são tratados como arrays.
Variáveis sem tipo ou com tipo mixed
obterão o valor JSON definido diretamente, sem qualquer conversão.
Veja a documentação de tipo do phpdoc para mais informações.
Observação
Este recurso está desabilitado por padrão por motivos de segurança desde a versão 5. Consulte $bStrictObjectTypeChecking para obter detalhes.
Quando um objeto deve ser criado, mas o JSON contém apenas um tipo simples (por exemplo, string, float, boolean), esse valor é passado para o construtor da classe. Exemplo:
Código PHP:
public DateTime $ date ;
JSON:
{ "date" : "2014-05-15" }
Isso resultará na chamada de new DateTime('2014-05-15')
.
Quando variáveis são definidas como objetos de classes ou interfaces abstratas, o JsonMapper normalmente tentaria instancia-las diretamente e travaria.
Usando a propriedade $classMap
do JsonMapper, você pode especificar quais classes serão instanciadas:
$ jm = new JsonMapper ();
$ jm -> classMap [ ' Foo ' ] = ' Bar ' ;
$ jm -> map (...);
Isso criaria objetos do tipo Bar
quando uma variável fosse definida como do tipo Foo
.
Também é possível usar um callable caso a classe de implementação real precise ser determinada dinamicamente (por exemplo, no caso de uma união). A classe mapeada ('Foo' no exemplo abaixo) e os dados Json são passados como parâmetros para a chamada.
$ mapper = function ( $ class , $ jvalue ) {
// examine $class and $jvalue to figure out what class to use...
return ' DateTime ' ;
};
$ jm = new JsonMapper ();
$ jm -> classMap [ ' Foo ' ] = $ mapper ;
$ jm -> map (...);
JsonMapper lança uma exceção quando uma propriedade JSON é null
, a menos que a propriedade da classe PHP tenha um tipo anulável - por exemplo, Contact|null
ou ?Contact
.
Se sua API contém muitos campos que podem ser null
e você não deseja tornar todas as suas definições de tipo anuláveis, defina:
$ jm -> bStrictNullTypes = false ;
Desde a versão 5.0.0, valores null
em arrays levam a um JsonMapper_Exception
a menos que o tipo seja anulável - por exemplo, array[?string]
ou array[string|null]
.
Para recuperar o comportamento anterior (permitindo nulos mesmo quando não declarado assim), defina:
$ jm -> bStrictNullTypesInArrays = false ;
O método setLogger()
do JsonMapper suporta todas as instâncias de logger compatíveis com PSR-3.
Eventos que são registrados:
Durante o desenvolvimento, as APIs mudam frequentemente. Para ser notificado sobre tais mudanças, o JsonMapper pode ser configurado para lançar exceções em caso de dados ausentes ou ainda desconhecidos.
Quando JsonMapper vê propriedades nos dados JSON que não estão definidas na classe PHP, você pode permitir que ele lance uma exceção definindo $bExceptionOnUndefinedProperty
:
$ jm = new JsonMapper ();
$ jm -> bExceptionOnUndefinedProperty = true ;
$ jm -> map (...);
Você também pode optar por lidar com essas propriedades definindo um callable como $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
*/
function setUndefinedProperty ( $ object , $ propName , $ jsonValue )
{
$ object ->{ ' UNDEF ' . $ propName } = $ jsonValue ;
}
$ jm = new JsonMapper ();
$ jm -> undefinedPropertyHandler = ' setUndefinedProperty ' ;
$ jm -> map (...);
Ou se você deixar o JsonMapper lidar com o setter para você, você pode retornar uma string do $undefinedPropertyHandler
que será usada como nome da propriedade.
/**
* 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
*/
function fixPropName ( $ object , $ propName , $ jsonValue )
{
return ucfirst ( $ propName );
}
$ jm = new JsonMapper ();
$ jm -> undefinedPropertyHandler = ' fixPropName ' ;
$ jm -> map (...);
Observação
Isso só funciona quando $bStrictObjectTypeChecking permanece ativado.
Propriedades em suas classes PHP podem ser marcadas como “obrigatórias” colocando @required
em seu docblock:
/**
* @var string
* @required
*/
public $ someDatum ;
Quando os dados JSON não contêm esta propriedade, o JsonMapper lançará um JsonMapper_Exception
quando $bExceptionOnMissingData
for ativado:
$ jm = new JsonMapper ();
$ jm -> bExceptionOnMissingData = true ;
$ jm -> map (...);
A opção $bRemoveUndefinedAttributes
faz com que o JsonMapper remova propriedades do objeto final se elas não estiverem nos dados JSON:
$ jm = new JsonMapper ();
$ jm -> bRemoveUndefinedAttributes = true ;
$ jm -> map (...);
Você pode permitir o mapeamento para propriedades privadas e protegidas e métodos setter definindo $bIgnoreVisibility
como true:
$ jm = new JsonMapper ();
$ jm -> bIgnoreVisibility = true ;
$ jm -> map (...);
Quando o tipo de uma variável é uma classe e os dados JSON são um tipo simples como string
, o JsonMapper pode passar esse valor para o construtor da classe quando configurado para isso:
$ jm = new JsonMapper ();
$ jm -> bStrictObjectTypeChecking = false ;
$ jm -> map (...);
Isso pode ser usado para inicializar automaticamente objetos DateTime a partir de strings de data.
Porém, desabilitar essas verificações estritas de tipo de objeto pode levar a problemas:
@required
não serão preenchidasObservação
O valor padrão mudou de false
para true
na versão 5 para aumentar a segurança.
Agora você precisa aceitar se quiser passar tipos simples para o construtor da classe.
map()
Você pode querer passar dados do array para map()
que você obteve chamando
json_decode ( $ jsonString , true )
Por padrão, JsonMapper lançará uma exceção porque map()
requer um objeto como primeiro parâmetro. Você pode contornar isso definindo $bEnforceMapType
como false
:
$ jm = new JsonMapper ();
$ jm -> bEnforceMapType = false ;
$ jm -> map (...);
JsonMapper é capaz de chamar um método personalizado diretamente em cada objeto após a conclusão do mapeamento:
$ jm = new JsonMapper ();
$ jm -> postMappingMethod = ' afterMapping ' ;
$ jm -> map (...);
Agora afterMapping()
é chamado em cada objeto mapeado (se a classe tiver esse método).
Você pode passar argumentos adicionais para o retorno de chamada pós-mapeamento:
$ jm = new JsonMapper ();
$ jm -> postMappingMethod = ' afterMapping ' ;
$ jm -> postMappingMethodArguments = [ 23 , ' foo ' ];
$ jm -> map (...);
Via Composer do Packagist:
$ compositor requer netresearch/jsonmapper
Alternativas
JsonMapper está licenciado sob a OSL 3.0.
JsonMapper segue os padrões de codificação PEAR.
Christian Weiske, cweiske.de