該庫目前處於維護模式,並由 erosb/json-sKema 取代。
這個存儲庫不會看到任何新功能。它為 JSON Schema 規範的草案 04、草案 06 和草案 07 版本提供了可靠的支援。
最新草案 2020-12 僅由 erosb/json-sKema 支持。
此專案是 JSON Schema Draft v4、Draft v6 和 Draft v7 規範的實作。它使用 org.json API(由 Douglas Crockford 創建)來表示 JSON 資料。
假設您已經知道 JSON Schema 是什麼,並且希望在 Java 應用程式中使用它來驗證 JSON 資料。但是 - 正如您可能已經發現的 - JSON Schema 規範還有其他 Java 實作。因此,這裡有一些關於使用哪一種的建議:
將以下依賴項新增至您的pom.xml
:
< dependency >
< groupId >com.github.erosb</ groupId >
< artifactId >everit-json-schema</ artifactId >
< version >1.14.4</ version >
</ dependency >
有關舊版本的注意事項: 1.6.0
和1.9.1
之間的版本只能在具有com.github.everit-org.json-schema:org.everit.json.schema
座標的 JitPack 上找到。版本1.0.0
... 1.5.1
可在 Maven Central 上的org.everit.json:org.everit.json.schema
座標下找到。
為了讓該函式庫在 Java 6/7 上運行,我們進行了幾次嘗試。
版本 1.9.2 的 java6 埠由 @mindbender1 開發,可透過 Maven Central 訪問,座標如下:
< dependency >
< groupId >com.github.erosb</ groupId >
< artifactId >everit-json-schema-jdk6</ artifactId >
< version >1.9.2</ version >
</ dependency >
舊版的向後移植:
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
}
JSON Schema 目前有 4 個主要版本,Draft 3、Draft 4、Draft 6 和 Draft 7。由於這兩個版本有許多差異 - 並且草案 6 不向後相容草案 4 - 最好知道您將使用哪個版本。
表示要使用的 JSON 架構版本的最佳方法是使用"$schema"
鍵將其元架構 URL 包含在文件根目錄中。這是一種常見的表示法,由庫幫助確定應使用哪個版本。
快速參考:
"$schema": "http://json-schema.org/draft-04/schema"
,則會使用 Draft 4"$schema": "http://json-schema.org/draft-06/schema"
,則會使用 Draft 6"$schema": "http://json-schema.org/draft-07/schema"
,則會使用 Draft 7如果您想要明確指定元架構版本,則可以透過下列方式配置載入器,將預設值從 Draft 4 變更為 Draft 6 / 7:
SchemaLoader loader = SchemaLoader . builder ()
. schemaJson ( yourSchemaJSON )
. draftV6Support () // or draftV7Support()
. build ();
Schema schema = loader . load (). build ();
從版本1.1.0
開始,驗證器會收集每個架構違規(而不是在第一個違規時立即失敗)。每個失敗都由 JSON 指標表示,從文件的根指向違規部分。如果偵測到多個架構違規,則將在違規的最常見父元素處引發ValidationException
,並且可以使用ValidationException#getCausingExceptions()
方法取得每個單獨的違規。
為了示範上述概念,讓我們來看一個例子。讓我們考慮以下架構:
{
"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 " }
}
}
}
}
以下 JSON 文件僅存在一項違反架構的行為(因為「a」不能為負數):
{
"rectangle" : {
"a" : -5 ,
"b" : 5
}
}
在這種情況下,拋出的ValidationException
將指向#/rectangle/a
並且它不會包含子異常:
try {
schema . validate ( rectangleSingleFailure );
} catch ( ValidationException e ) {
// prints #/rectangle/a: -5.0 is not higher or equal to 0
System . out . println ( e . getMessage ());
}
現在 - 為了說明處理多個違規的方式 - 讓我們考慮以下 JSON 文檔,其中“a”和“b”屬性都違反了上述架構:
{
"rectangle" : {
"a" : -5 ,
"b" : " asd "
}
}
在這種情況下,拋出的ValidationException
將指向#/rectangle
,並且它有 2 個子異常,分別指向#/rectangle/a
和#/rectangle/b
:
try {
schema . validate ( rectangleMultipleFailures );
} catch ( ValidationException e ) {
System . out . println ( e . getMessage ());
e . getCausingExceptions (). stream ()
. map ( ValidationException :: getMessage )
. forEach ( System . out :: println );
}
這將列印以下輸出:
#/rectangle: 2 schema violations found
#/rectangle/a: -5.0 is not higher or equal to 0
#/rectangle/b: expected type: Number, found: String
從版本1.4.0
開始,可以將ValidationException
實例列印為 JSON 格式的失敗報告。 ValidationException#toJSON()
方法傳回一個有以下鍵的JSONObject
實例:
"message"
:程式設計師友善的異常訊息(驗證失敗的描述)"keyword"
: 被違反的 JSON Schema 關鍵字"pointerToViolation"
:一個 JSON 指針,表示從輸入文檔根到導致驗證失敗的片段的路徑"schemaLocation"
:一個 JSON 指針,表示從架構 JSON 根到違規關鍵字的路徑"causingExceptions"
:子異常的陣列(可能為空)。每個子異常都表示為一個 JSON 對象,其結構與此清單中描述的相同。請參閱上面有關引發異常的更多資訊。請注意,完整的故障報告是分層樹狀結構:可以使用#getCausingExceptions()
來取得原因的子原因。
ValidationListener
可以用來解決實例 JSON如何與架構匹配(或不匹配)的歧義。您可以將ValidationListener
實作附加到驗證器以接收有關中間成功/失敗結果的事件通知。
例子:
import org . everit . json . schema . Validator ;
...
Validator validator = Validator . builder ()
. withListener ( new YourValidationListenerImplementation ())
. build ();
validator . performValidation ( schema , input );
目前支援的事件:
"$ref"
引用"allOf"
/ "anyOf"
/ "oneOf"
模式符合下的子模式"allOf"
/ "anyOf"
/ "oneOf"
模式下的子模式無法匹配"if"
模式匹配"if"
模式無法匹配"then"
模式匹配"then"
模式無法匹配"else"
模式匹配"else"
模式無法匹配有關更多詳細信息,請參閱org.everit.json.schema.event.ValidationListener
介面的 javadoc。特定的事件類別還具有適當的#toJSON()
和#toString()
實現,因此您可以以易於解析的格式列印它們。
預設情況下,驗證錯誤以收集模式報告(請參閱「調查失敗」一章)。這對於獲得詳細的錯誤報告很方便,但在某些情況下,在發現失敗時停止驗證而不檢查 JSON 文件的其餘部分更合適。切換這種快速失敗的驗證模式
Validator
實例,而不是呼叫Schema#validate(input)
ValidatorBuilder
的failEarly()
方法例子:
import org . everit . json . schema . Validator ;
...
Validator validator = Validator . builder ()
. failEarly ()
. build ();
validator . performValidation ( schema , input );
注意: Validator
類是不可變的且線程安全的,因此您不必為每個驗證創建一個新的類,只需配置一次就足夠了。
在某些情況下,當驗證數字或布林值時,接受可解析為此類基元的字串值是有意義的,因為任何後續處理也會自動將這些文字解析為正確的數字和邏輯值。此外,非字串基元值轉換為字串很簡單,那麼為什麼不允許任何 json 基元作為字串呢?
例如,讓我們採用以下模式:
{
"properties" : {
"booleanProp" : {
"type" : " boolean "
},
"integerProp" : {
"type" : " integer "
},
"nullProp" : {
"type" : " null "
},
"numberProp" : {
"type" : " number "
},
"stringProp" : {
"type" : " string "
}
}
}
儘管所有字串都可以輕鬆轉換為適當的值,但以下 JSON 文件無法驗證:
{
"numberProp" : " 12.34 " ,
"integerProp" : " 12 " ,
"booleanProp" : " true " ,
"nullProp" : " null " ,
"stringProp" : 12.34
}
在這種情況下,如果您希望上述實例通過架構驗證,則需要使用開啟的寬鬆原始驗證配置。例子:
import org . everit . json . schema .*;
...
Validator validator = Validator . builder ()
. primitiveValidationStrategry ( PrimitiveValidationStrategy . LENIENT )
. build ();
validator . performValidation ( schema , input );
注意:在寬鬆的解析模式下,所有 22 個可能的布林文字都將被接受為邏輯值。
JSON Schema 規範定義了「default」關鍵字來表示預設值,儘管它沒有明確說明它應該如何影響驗證過程。預設情況下,該庫不會設定預設值,但如果您需要此功能,可以在載入架構之前透過SchemaLoaderBuilder#useDefaults(boolean)
方法將其開啟:
{
"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
如果input
中缺少一些在架構中具有"default"
值的屬性,則驗證器將在驗證期間設定它們。
為了支援 JSON Schema 的"regex"
關鍵字,該程式庫提供了兩種可能的實作:
java.util.regex
套件雖然 RE2J 庫提供了比java.util.regex
明顯更好的性能,但它與java.util
或ECMA 262 支援的語法並不完全相容。使用RE2J。
可以透過SchemaLoaderBuilder#regexpFactory()
啟動 RE2J 實作:
SchemaLoader loader = SchemaLoader . builder ()
. regexpFactory ( new RE2JRegexpFactory ())
// ...
. build ();
筆記:
pom.xml
中,這樣它就不會不必要地增加工件的大小java.util
實現,在1.8.0 中,使用了RE2J 實現,而在1.9.0 中,由於報告了一些回歸,我們使其可配置。 該程式庫支援首次出現在 Draft 7 中的readOnly
和writeOnly
關鍵字。例子:
架構.json:
{
"properties" : {
"id" : {
"type" : " number " ,
"readOnly" : true
}
}
}
驗證程式碼片段:
Validator validator = Validator . builder ()
. readWriteContext ( ReadWriteContext . WRITE )
. build ();
validator . performValidation ( schema , new JSONObject ( "{ " id " :42}" ));
在本例中,我們告訴驗證器驗證發生在WRITE
上下文中,並且在輸入 JSON 物件中出現"id"
屬性,該屬性在模式中標記為"readOnly"
,因此此呼叫將拋出ValidationException
。
從版本1.2.0
開始,函式庫支援"format"
關鍵字(這是規範的可選部分)。
支援的格式根據您使用的架構規範版本而有所不同(因為標準格式是在驗證規範的不同版本中引入的)。
以下是支援的標準格式的相容性表:
草案4 | 草案 6 | 草案 7 | |
---|---|---|---|
日期時間 | ✅ | ✅ | ✅ |
電子郵件 | ✅ | ✅ | ✅ |
主機名稱 | ✅ | ✅ | ✅ |
ipv4 | ✅ | ✅ | ✅ |
ipv6 | ✅ | ✅ | ✅ |
烏裡 | ✅ | ✅ | ✅ |
uri 引用 | ✅ | ✅ | |
uri 模板 | ✅ | ✅ | |
json 指針 | ✅ | ✅ | |
日期 | ✅ | ||
時間 | ✅ | ||
正規表示式 | ✅ | ||
相對 json 指針 | ✅ |
該庫還支援添加自訂格式驗證器。要使用自訂驗證器基本上你必須
org.everit.json.schema.FormatValidator
介面的類別中建立您自己的驗證org.everit.json.schema.loader.SchemaLoader.SchemaLoaderBuilder
實例中的名稱讓我們假設任務是創建一個自訂驗證器,它接受具有偶數個字元的字串。
自訂FormatValidator
將如下所示:
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 ));
}
}
}
要將EvenCharNumValidator
綁定到"format"
值(例如"evenlength"
),您必須將驗證器實例綁定到模式載入器配置中的關鍵字:
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
在 JSON 架構文件中,可以使用相對 URI 來引用先前定義的類型。此類引用使用"$ref"
和"$id"
關鍵字來表達。雖然該規範詳細描述了解析範圍更改和取消引用,但它沒有解釋當第一次出現的"$ref"
或"$id"
是相對 URI 時的預期行為。
在此實作的情況下,可以使用適當的建構器方法明確定義用作基本 URI(解析範圍)的絕對 URI:
SchemaLoader schemaLoader = SchemaLoader . builder ()
. schemaJson ( jsonSchema )
. resolutionScope ( "http://example.org/" ) // setting the default resolution scope
. build ();
隨著模式的成長,您將需要將其拆分為多個來源文件,並將它們與"$ref"
引用連接。如果您想要將模式儲存在類別路徑上(而不是透過 HTTP 提供服務),那麼建議的方法是使用classpath:
協定使模式相互引用。要讓classpath:
協定起作用:
SchemaClient
,例如: SchemaLoader schemaLoader = SchemaLoader . builder ()
. schemaClient ( SchemaClient . classPathAwareClient ())
. schemaJson ( jsonSchema )
. resolutionScope ( "classpath://my/schemas/directory/" ) // setting the default resolution scope
. build ();
鑑於此配置,以下引用將在jsonSchema
中正確解析:
{
"properties" : {
"sameDir" : { "$ref" : " sameDirSchema.json " },
"absPath" : { "$ref" : " classpath://somewhere/else/otherschema.json " },
"httpPath" : { "$ref" : " http://example.org/http-works-as-usual " },
}
}
並會在類路徑上的/my/schemas/directory/sameDirSchema.json
中尋找sameDirSchema.json
。
有時使用預先載入的模式很有用,我們可以為其指派任意 URI(可能是 uuid),而不是透過 URL 載入模式。這可以透過使用模式載入器的#registerSchemaByURI()
方法將模式指派給 URI 來完成。例子:
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 ();
筆記:
JSONObject
或Boolean
(形式參數類型只是Object
,因為這兩個沒有任何其他公共超類別)。SchemaClient
實作也可以,或者您甚至可以利用java.net
套件的可擴充協定處理) 一些依賴項可以從庫中排除,但它仍然可用,但有一些限制:
com.damnhandy:handy-uri-templates
依賴項,那麼您的架構不應使用"uri-template"
格式commons-validator:commons-validator
依賴項,那麼您的架構不應使用下列格式: "email"
、 "ipv4"
、 "ipv6"
、 "hostname"
按庫版本:
產生的版本 1.0.0 - 1.5.1 的 javadoc 可在 javadoc.io 上取得
對於介於 (1.6.0 - 1.9.1) 之間的版本,它不會在任何地方發布。