Esta biblioteca está atualmente em modo de manutenção e foi substituída por erosb/json-sKema.
Este repositório não verá nenhum recurso novo. Ele fornece suporte sólido para versões draft-04, draft-06 e draft-07 da especificação do esquema JSON.
O último rascunho 2020-12 é suportado apenas por erosb/json-sKema.
Este projeto é uma implementação das especificações JSON Schema Draft v4, Draft v6 e Draft v7. Ele usa a API org.json (criada por Douglas Crockford) para representar dados JSON.
Vamos supor que você já saiba o que é JSON Schema e deseja utilizá-lo em um aplicativo Java para validar dados JSON. Mas - como você já deve ter descoberto - há também uma outra implementação Java da especificação do esquema JSON. Então aqui estão alguns conselhos sobre qual usar:
Adicione a seguinte dependência ao seu pom.xml
:
< dependency >
< groupId >com.github.erosb</ groupId >
< artifactId >everit-json-schema</ artifactId >
< version >1.14.4</ version >
</ dependency >
Nota sobre versões mais antigas : versões entre 1.6.0
e 1.9.1
só podem ser encontradas no JitPack com coordenadas com.github.everit-org.json-schema:org.everit.json.schema
. As versões 1.0.0
... 1.5.1
estão disponíveis no Maven Central nas coordenadas org.everit.json:org.everit.json.schema
.
Houve algumas tentativas de fazer a biblioteca funcionar em Java 6/7.
Uma porta java6 da versão 1.9.2 foi desenvolvida por @mindbender1 e pode ser acessada através do Maven Central com as seguintes coordenadas:
< dependency >
< groupId >com.github.erosb</ groupId >
< artifactId >everit-json-schema-jdk6</ artifactId >
< version >1.9.2</ version >
</ dependency >
Backports de versões mais antigas:
com.doctusoft:json-schema-java7:1.4.1
com.github.rdruilhe.json-schema:org.everit.json.schema:1.1.1
import org . everit . json . schema . Schema ;
import org . everit . json . schema . loader . SchemaLoader ;
import org . json . JSONObject ;
import org . json . JSONTokener ;
// ...
try ( InputStream inputStream = getClass (). getResourceAsStream ( "/path/to/your/schema.json" )) {
JSONObject rawSchema = new JSONObject ( new JSONTokener ( inputStream ));
Schema schema = SchemaLoader . load ( rawSchema );
schema . validate ( new JSONObject ( "{ " hello " : " world " }" )); // throws a ValidationException if this object is invalid
}
O esquema JSON tem atualmente 4 versões principais, Rascunho 3, Rascunho 4, Rascunho 6 e Rascunho 7. Esta biblioteca implementa os 3 mais recentes, você pode dar uma olhada rápida nas diferenças aqui e aqui. Como as duas versões têm diversas diferenças - e o rascunho 6 não é compatível com versões anteriores do rascunho 4 - é bom saber qual versão você usará.
A melhor maneira de indicar a versão do esquema JSON que você deseja usar é incluir sua URL do meta-esquema na raiz do documento com a chave "$schema"
. Esta é uma notação comum, facilitada pela biblioteca para determinar qual versão deve ser usada.
Referência rápida:
"$schema": "http://json-schema.org/draft-04/schema"
na raiz do esquema, então o Rascunho 4 será usado"$schema": "http://json-schema.org/draft-06/schema"
na raiz do esquema, então o Rascunho 6 será usado"$schema": "http://json-schema.org/draft-07/schema"
na raiz do esquema, então o Rascunho 7 será usadoSe você deseja especificar explicitamente a versão do meta-esquema, você pode alterar o padrão de Rascunho 4 para Rascunho 6/7 configurando o carregador desta forma:
SchemaLoader loader = SchemaLoader . builder ()
. schemaJson ( yourSchemaJSON )
. draftV6Support () // or draftV7Support()
. build ();
Schema schema = loader . load (). build ();
A partir da versão 1.1.0
o validador coleta todas as violações do esquema (em vez de falhar imediatamente na primeira). Cada falha é indicada por um ponteiro JSON, apontando da raiz do documento para a parte violadora. Se mais de uma violação de esquema tiver sido detectada, uma ValidationException
será lançada nos elementos pais mais comuns das violações, e cada violação separada poderá ser obtida usando o método ValidationException#getCausingExceptions()
.
Para demonstrar os conceitos acima, vejamos um exemplo. Vamos considerar o seguinte esquema:
{
"type" : " object " ,
"properties" : {
"rectangle" : { "$ref" : " #/definitions/Rectangle " }
},
"definitions" : {
"size" : {
"type" : " number " ,
"minimum" : 0
},
"Rectangle" : {
"type" : " object " ,
"properties" : {
"a" : { "$ref" : " #/definitions/size " },
"b" : { "$ref" : " #/definitions/size " }
}
}
}
}
O seguinte documento JSON tem apenas uma violação do esquema (já que "a" não pode ser negativo):
{
"rectangle" : {
"a" : -5 ,
"b" : 5
}
}
Neste caso, a ValidationException
lançada apontará para #/rectangle/a
e não conterá subexceções:
try {
schema . validate ( rectangleSingleFailure );
} catch ( ValidationException e ) {
// prints #/rectangle/a: -5.0 is not higher or equal to 0
System . out . println ( e . getMessage ());
}
Agora - para ilustrar a forma como múltiplas violações são tratadas - vamos considerar o seguinte documento JSON, onde as propriedades "a" e "b" violam o esquema acima:
{
"rectangle" : {
"a" : -5 ,
"b" : " asd "
}
}
Neste caso, a ValidationException
lançada apontará para #/rectangle
, e tem 2 sub-exceções, apontando para #/rectangle/a
e #/rectangle/b
:
try {
schema . validate ( rectangleMultipleFailures );
} catch ( ValidationException e ) {
System . out . println ( e . getMessage ());
e . getCausingExceptions (). stream ()
. map ( ValidationException :: getMessage )
. forEach ( System . out :: println );
}
Isso imprimirá a seguinte saída:
#/rectangle: 2 schema violations found
#/rectangle/a: -5.0 is not higher or equal to 0
#/rectangle/b: expected type: Number, found: String
Desde a versão 1.4.0
é possível imprimir as instâncias ValidationException
como relatórios de falhas no formato JSON. O método ValidationException#toJSON()
retorna uma instância JSONObject
com as seguintes chaves:
"message"
: a mensagem de exceção amigável ao programador (descrição da falha de validação)"keyword"
: a palavra-chave do esquema JSON que foi violada"pointerToViolation"
: um ponteiro JSON que indica o caminho da raiz do documento de entrada até seu fragmento que causou a falha de validação"schemaLocation"
: um ponteiro JSON que indica o caminho da raiz JSON do esquema até a palavra-chave violada"causingExceptions"
: uma matriz (possivelmente vazia) de subexceções. Cada subexceção é representada como um objeto JSON, com a mesma estrutura descrita nesta listagem. Veja mais acima sobre como causar exceções. Tenha em mente que o relatório de falha completo é uma estrutura de árvore hierárquica : as subcausas de uma causa podem ser obtidas usando #getCausingExceptions()
.
ValidationListener
s podem servir ao propósito de resolver a ambigüidade sobre como uma instância JSON corresponde (ou não) a um esquema. Você pode anexar uma implementação ValidationListener
ao validador para receber notificações de eventos sobre resultados intermediários de sucesso/falha.
Exemplo:
import org . everit . json . schema . Validator ;
...
Validator validator = Validator . builder ()
. withListener ( new YourValidationListenerImplementation ())
. build ();
validator . performValidation ( schema , input );
Os eventos atualmente suportados:
"$ref"
sendo resolvida"allOf"
/ "anyOf"
/ "oneOf"
"allOf"
/ "anyOf"
/ "oneOf"
que não corresponde"if"
"if"
não corresponde"then"
"then"
não corresponde"else"
"else"
não corresponde Consulte o javadoc da interface org.everit.json.schema.event.ValidationListener
para obter mais detalhes. As classes de eventos específicas também possuem implementações #toJSON()
e #toString()
adequadas para que você possa imprimi-las em um formato facilmente analisável.
Por padrão, o erro de validação é relatado no modo de coleta (consulte o capítulo "Investigando falhas"). Isso é conveniente para ter um relatório de erros detalhado, mas em algumas circunstâncias é mais apropriado interromper a validação quando uma falha é encontrada sem verificar o restante do documento JSON. Para alternar este modo de validação com falha rápida
Validator
para o seu esquema em vez de chamar Schema#validate(input)
failEarly()
do ValidatorBuilder
Exemplo:
import org . everit . json . schema . Validator ;
...
Validator validator = Validator . builder ()
. failEarly ()
. build ();
validator . performValidation ( schema , input );
Nota: a classe Validator
é imutável e thread-safe, portanto não é necessário criar uma nova para cada validação, basta configurá-la apenas uma vez.
Em alguns casos, ao validar números ou booleanos, faz sentido aceitar valores de string que podem ser analisados como primitivos, porque qualquer processamento sucessivo também analisará automaticamente esses literais em valores numéricos e lógicos adequados. Além disso, valores primitivos não-string são triviais para serem convertidos em strings, então por que não permitir quaisquer primitivos json como strings?
Por exemplo, vamos pegar este esquema:
{
"properties" : {
"booleanProp" : {
"type" : " boolean "
},
"integerProp" : {
"type" : " integer "
},
"nullProp" : {
"type" : " null "
},
"numberProp" : {
"type" : " number "
},
"stringProp" : {
"type" : " string "
}
}
}
O seguinte documento JSON não é validado, embora todas as strings possam ser facilmente convertidas em valores apropriados:
{
"numberProp" : " 12.34 " ,
"integerProp" : " 12 " ,
"booleanProp" : " true " ,
"nullProp" : " null " ,
"stringProp" : 12.34
}
Nesse caso, se você quiser que a instância acima passe na validação em relação ao esquema, será necessário usar a configuração de validação primitiva tolerante ativada. Exemplo:
import org . everit . json . schema .*;
...
Validator validator = Validator . builder ()
. primitiveValidationStrategry ( PrimitiveValidationStrategy . LENIENT )
. build ();
validator . performValidation ( schema , input );
Nota: no modo de análise tolerante, todos os 22 literais booleanos possíveis serão aceitos como valores lógicos.
A especificação do esquema JSON define a palavra-chave "default" para denotar valores padrão, embora não declare explicitamente como isso deve afetar o processo de validação. Por padrão esta biblioteca não define os valores padrão, mas se você precisar deste recurso, você pode ativá-lo pelo método SchemaLoaderBuilder#useDefaults(boolean)
, antes de carregar o esquema:
{
"properties" : {
"prop" : {
"type" : " number " ,
"default" : 1
}
}
}
JSONObject input = new JSONObject ( "{}" );
System . out . println ( input . get ( "prop" )); // prints null
Schema schema = SchemaLoader . builder ()
. useDefaults ( true )
. schemaJson ( rawSchema )
. build ()
. load (). build ();
schema . validate ( input );
System . out . println ( input . get ( "prop" )); // prints 1
Se houver algumas propriedades faltando na input
que possuem valores "default"
no esquema, elas serão definidas pelo validador durante a validação.
Para suportar a palavra-chave "regex"
do esquema JSON, a biblioteca oferece duas implementações possíveis:
java.util.regex
Embora a biblioteca RE2J forneça desempenho significativamente melhor que java.util.regex
, ela não é totalmente compatível com a sintaxe suportada por java.util
ou ECMA 262. Portanto, RE2J é recomendado se você estiver preocupado com o desempenho e suas limitações forem aceitáveis.
A implementação RE2J pode ser ativada com a chamada SchemaLoaderBuilder#regexpFactory()
:
SchemaLoader loader = SchemaLoader . builder ()
. regexpFactory ( new RE2JRegexpFactory ())
// ...
. build ();
Notas:
pom.xml
para não aumentar desnecessariamente o tamanho do seu artefatojava.util
, na 1.8.0 foi utilizada a implementação RE2J, e na 1.9.0 a tornamos configurável, devido a algumas regressões relatadas. A biblioteca suporta as palavras-chave readOnly
e writeOnly
que apareceram pela primeira vez no Rascunho 7. Se você quiser utilizar esse recurso, antes da validação você precisará informar ao validador se a validação ocorre no contexto de leitura ou gravação. Exemplo:
esquema.json:
{
"properties" : {
"id" : {
"type" : " number " ,
"readOnly" : true
}
}
}
Snippet de código de validação:
Validator validator = Validator . builder ()
. readWriteContext ( ReadWriteContext . WRITE )
. build ();
validator . performValidation ( schema , new JSONObject ( "{ " id " :42}" ));
Neste caso informamos ao validador que a validação acontece no contexto WRITE
, e no objeto JSON de entrada aparece a propriedade "id"
, que está marcada como "readOnly"
no esquema, portanto esta chamada lançará uma ValidationException
.
A partir da versão 1.2.0
a biblioteca suporta a palavra-chave "format"
(que é uma parte opcional da especificação).
Os formatos suportados variam dependendo da versão da especificação do esquema usada (já que os formatos padrão foram introduzidos em diferentes versões na especificação de validação).
Aqui está uma tabela de compatibilidade dos formatos padrão suportados:
Rascunho 4 | Rascunho 6 | Rascunho 7 | |
---|---|---|---|
data-hora | ✅ | ✅ | ✅ |
✅ | ✅ | ✅ | |
nome do host | ✅ | ✅ | ✅ |
ipv4 | ✅ | ✅ | ✅ |
ipv6 | ✅ | ✅ | ✅ |
uri | ✅ | ✅ | ✅ |
referência uri | ✅ | ✅ | |
modelo uri | ✅ | ✅ | |
ponteiro json | ✅ | ✅ | |
data | ✅ | ||
tempo | ✅ | ||
expressão regular | ✅ | ||
ponteiro json relativo | ✅ |
A biblioteca também oferece suporte à adição de validadores de formato personalizado. Para usar um validador personalizado basicamente você precisa
org.everit.json.schema.FormatValidator
org.everit.json.schema.loader.SchemaLoader.SchemaLoaderBuilder
antes de carregar o esquema realVamos supor que a tarefa seja criar um validador customizado que aceite strings com um número par de caracteres.
O FormatValidator
personalizado será parecido com isto:
public class EvenCharNumValidator implements FormatValidator {
@ Override
public Optional < String > validate ( final String subject ) {
if ( subject . length () % 2 == 0 ) {
return Optional . empty ();
} else {
return Optional . of ( String . format ( "the length of string [%s] is odd" , subject ));
}
}
}
Para vincular EvenCharNumValidator
a um valor "format"
(por exemplo "evenlength"
), você deve vincular uma instância do validador à palavra-chave na configuração do carregador de esquema:
JSONObject rawSchema = new JSONObject ( new JSONTokener ( inputStream ));
SchemaLoader schemaLoader = SchemaLoader . builder ()
. schemaJson ( rawSchema ) // rawSchema is the JSON representation of the schema utilizing the "evenlength" non-standard format
. addFormatValidator ( "evenlength" , new EvenCharNumValidator ()) // the EvenCharNumValidator gets bound to the "evenlength" keyword
. build ();
Schema schema = schemaLoader . load (). build (); // the schema is created using the above created configuration
schema . validate ( jsonDocument ); // the document validation happens here
Em um documento JSON Schema é possível usar URIs relativos para referir tipos previamente definidos. Tais referências são expressas usando as palavras-chave "$ref"
e "$id"
. Embora a especificação descreva detalhadamente a alteração do escopo de resolução e a desreferenciação, ela não explica o comportamento esperado quando o primeiro "$ref"
ou "$id"
que ocorre é um URI relativo.
No caso desta implementação é possível definir explicitamente um URI absoluto servindo como URI base (escopo de resolução) usando o método construtor apropriado:
SchemaLoader schemaLoader = SchemaLoader . builder ()
. schemaJson ( jsonSchema )
. resolutionScope ( "http://example.org/" ) // setting the default resolution scope
. build ();
À medida que seus esquemas crescem, você desejará dividi-los em vários arquivos de origem e conectá-los com referências "$ref"
. Se você deseja armazenar os esquemas no caminho de classe (em vez de, por exemplo, servi-los por meio de HTTP), a maneira recomendada é usar o protocolo classpath:
para fazer com que os esquemas façam referência uns aos outros. Para fazer o protocolo classpath:
funcionar:
SchemaClient
integrado com reconhecimento de classpath da biblioteca, por exemplo: SchemaLoader schemaLoader = SchemaLoader . builder ()
. schemaClient ( SchemaClient . classPathAwareClient ())
. schemaJson ( jsonSchema )
. resolutionScope ( "classpath://my/schemas/directory/" ) // setting the default resolution scope
. build ();
Dada esta configuração, as seguintes referências serão devidamente resolvidas no jsonSchema
:
{
"properties" : {
"sameDir" : { "$ref" : " sameDirSchema.json " },
"absPath" : { "$ref" : " classpath://somewhere/else/otherschema.json " },
"httpPath" : { "$ref" : " http://example.org/http-works-as-usual " },
}
}
e sameDirSchema.json
será procurado em /my/schemas/directory/sameDirSchema.json
no caminho de classe.
Às vezes é útil trabalhar com esquemas pré-carregados, aos quais atribuímos um URI arbitrário (talvez um uuid) em vez de carregar o esquema através de uma URL. Isso pode ser feito atribuindo os esquemas a um URI com o método #registerSchemaByURI()
do carregador de esquema. Exemplo:
SchemaLoader schemaLoader = SchemaLoader . builder ()
. registerSchemaByURI ( new URI ( "urn:uuid:a773c7a2-1a13-4f6a-a70d-694befe0ce63" ), aJSONObject )
. registerSchemaByURI ( new URI ( "http://example.org" ), otherJSONObject )
. schemaJson ( jsonSchema )
. resolutionScope ( "classpath://my/schemas/directory/" )
. build ();
Notas:
JSONObject
ou Boolean
(o tipo de parâmetro formal é Object
apenas porque esses dois não têm nenhuma outra superclasse comum).SchemaClient
também funcione, ou você pode até utilizar o tratamento de protocolo extensível do pacote java.net
) Algumas das dependências podem ser excluídas da biblioteca e ela ainda permanece utilizável, com algumas limitações:
com.damnhandy:handy-uri-templates
, seu esquema não deverá usar o formato "uri-template"
commons-validator:commons-validator
, seu esquema não deverá usar os seguintes formatos: "email"
, "ipv4"
, "ipv6"
, "hostname"
Por versão da biblioteca:
O javadoc gerado das versões 1.0.0 - 1.5.1 está disponível em javadoc.io
Para as versões intermediárias (1.6.0 - 1.9.1) não é publicado em lugar nenhum.