for-get / jesse Goto Github PK
View Code? Open in Web Editor NEWjesse (JSon Schema Erlang) is an implementation of a JSON Schema validator for Erlang.
Home Page: https://github.com/for-get/jesse
License: Apache License 2.0
jesse (JSon Schema Erlang) is an implementation of a JSON Schema validator for Erlang.
Home Page: https://github.com/for-get/jesse
License: Apache License 2.0
I noticed that although 1.5.0 release is mentioned almost in the changelog a year ago the corresponding tag is missing
I tried to minimize test case, but still not sure if it's minimal enough:
-module(jesse_test).
-export([test/0]).
test() ->
Schema1 =
#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> =>
#{<<"my_local_ref">> => #{
<<"properties">> => #{
<<"my_prop">> => #{
<<"$ref">> => <<"schema2#definitions/my_remote_ref">>
}
}
}},
<<"properties">> => #{
<<"my_list">> =>
#{<<"items">> =>
#{<<"$ref">> => <<"#definitions/my_local_ref">>}}}},
Schema2 =
#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> =>
#{<<"my_remote_ref">> => #{}}},
JSON = #{<<"my_list">> => [#{<<"my_prop">> => null},
#{<<"my_prop">> => null}]},
jesse:add_schema("schema1", Schema1),
jesse:add_schema("schema2", Schema2),
io:format("~s~n~s~n~s~n", [jiffy:encode(J, [pretty]) || J <- [Schema1, Schema2, JSON]]),
jesse:validate("schema1", JSON).
> jesse_test:test().
{
"properties" : {
"my_list" : {
"items" : {
"$ref" : "#definitions/my_local_ref"
}
}
},
"definitions" : {
"my_local_ref" : {
"properties" : {
"my_prop" : {
"$ref" : "schema2#definitions/my_remote_ref"
}
}
}
},
"$schema" : "http://json-schema.org/draft-04/schema#"
}
{
"definitions" : {
"my_remote_ref" : {
}
},
"$schema" : "http://json-schema.org/draft-04/schema#"
}
{
"my_list" : [
{
"my_prop" : null
},
{
"my_prop" : null
}
]
}
{error,[{schema_invalid,#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
schema_invalid}]}
It says 'schema invalid', while it's ok.
Here are part of tracer output. Maybe it can help somehow
dbg:tracer(), dbg:tpl(jesse_state, []), dbg:tpl(jesse_error, []), dbg:tpl(jesse_schema_validator, []), dbg:p(spawn_link(fun() -> timer:sleep(1), exit(jesse_test:test()) end), [c, m]).
(<0.18340.0>) call jesse_schema_validator:select_and_run_validator(<<"http://json-schema.org/draft-04/schema#">>,#{<<"$ref">> => <<"#definitions/my_local_ref">>},#{<<"my_prop">> => null},{state,#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> => #{<<"my_remote_ref">> => #{}}},
#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
[1,<<"my_list">>],
0,[],#Fun<jesse_error.default_error_handler.3>,
<<"http://json-schema.org/draft-04/schema#">>,
#Fun<jesse_database.read.1>,<<"schema2">>})
(<0.18340.0>) call jesse_state:resolve_reference({state,#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> => #{<<"my_remote_ref">> => #{}}},
#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
[1,<<"my_list">>],
0,[],#Fun<jesse_error.default_error_handler.3>,
<<"http://json-schema.org/draft-04/schema#">>,
#Fun<jesse_database.read.1>,<<"schema2">>},<<"#definitions/my_local_ref">>)
(<0.18340.0>) call jesse_state:combine_id(<<"schema2">>,<<"#definitions/my_local_ref">>)
(<0.18340.0>) call jesse_state:combine_relative_id(<<"schema2">>,"#definitions/my_local_ref")
(<0.18340.0>) call jesse_state:find_schema({state,#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> => #{<<"my_remote_ref">> => #{}}},
#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
[1,<<"my_list">>],
0,[],#Fun<jesse_error.default_error_handler.3>,
<<"http://json-schema.org/draft-04/schema#">>,
#Fun<jesse_database.read.1>,<<"schema2">>},<<"schema2">>)
(<0.18340.0>) call jesse_state:find_schema({state,#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> => #{<<"my_remote_ref">> => #{}}},
#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
[1,<<"my_list">>],
0,[],#Fun<jesse_error.default_error_handler.3>,
<<"http://json-schema.org/draft-04/schema#">>,
#Fun<jesse_database.read.1>,<<"schema2">>},"schema2")
(<0.18340.0>) call jesse_state:local_schema(#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> => #{<<"my_remote_ref">> => #{}}},[<<"definitions">>,<<"my_local_ref">>])
(<0.18340.0>) call jesse_state:local_schema(#{<<"my_remote_ref">> => #{}},[<<"my_local_ref">>])
(<0.18340.0>) call jesse_state:local_schema(not_found,[])
(<0.18340.0>) call jesse_error:handle_schema_invalid(schema_invalid,{state,#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> => #{<<"my_remote_ref">> => #{}}},
#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
[1,<<"my_list">>],
0,[],#Fun<jesse_error.default_error_handler.3>,
<<"http://json-schema.org/draft-04/schema#">>,
#Fun<jesse_database.read.1>,<<"schema2">>})
(<0.18340.0>) call jesse_state:get_current_schema({state,#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> => #{<<"my_remote_ref">> => #{}}},
#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
[1,<<"my_list">>],
0,[],#Fun<jesse_error.default_error_handler.3>,
<<"http://json-schema.org/draft-04/schema#">>,
#Fun<jesse_database.read.1>,<<"schema2">>})
(<0.18340.0>) call jesse_error:handle_error({schema_invalid,#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
schema_invalid},{state,#{<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"definitions">> => #{<<"my_remote_ref">> => #{}}},
#{<<"properties">> => #{<<"my_prop">> => #{<<"$ref">> => <<"schema2#definitions/my_remote_ref">>}}},
[1,<<"my_list">>],
0,[],#Fun<jesse_error.default_error_handler.3>,
<<"http://json-schema.org/draft-04/schema#">>,
#Fun<jesse_database.read.1>,<<"schema2">>})
It seems that starting from 820d0e9#diff-06b055de1c1c5e19892ec0ef0a7d057f, jesse stores schemas in its database using canonical keys, which, unfortunately, depend on the current working directory (see
Line 345 in 718d95d
In particular, that means this:
1> jesse:add_schema(key, {[]}).
ok
2> jesse:validate(key, {[]}).
{ok,{[]}}
3> file:set_cwd("/").
ok
4> jesse:validate(key, {[]}).
{error,{database_error,"file:///key",schema_not_found}}
which I think should be regarded as a bug?
Two questions here:
Thanks! :)
draft-wright-json-schema-00 (Internet-Draft October 13, 2016) | 8.2.1. Internal references states that:
Schemas can be identified by any URI that has been given to them, including a JSON Pointer or their URI given directly by "id".
In particular, the following schema will not work with jesse
:
{
"id": "http://example.net/root.json",
"items": {
"type": "array",
"items": { "$ref": "#item" }
},
"definitions": {
"single": {
"id": "#item",
"type": "integer"
}
}
}
Failing example with internal reference URI given directly by "id" (#item
):
1> jesse:add_schema("http://example.net/root.json", <<"{\"id\":\"http://example.net/root.json\",\"items\":{\"type\":\"array\",\"items\":{\"$ref\":\"#item\"}},\"definitions\":{\"single\":{\"id\":\"#item\",\"type\":\"integer\"}}}">>, [{parser_fun, fun jsx:decode/1}]).
ok
2> jesse:validate("http://example.net/root.json", <<"[[1, 2, 3]]">>, [{parser_fun, fun jsx:decode/1}]).
{error,[{schema_invalid,[{<<"$ref">>,<<"#item">>}],
schema_invalid}]}
Successful example with internal reference given as JSON Pointer (#/definitions/single
):
1> jesse:add_schema("http://example.net/root.json", <<"{\"id\":\"http://example.net/root.json\",\"items\":{\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/single\"}},\"definitions\":{\"single\":{\"id\":\"#item\",\"type\":\"integer\"}}}">>, [{parser_fun, fun jsx:decode/1}]).
ok
2> jesse:validate("http://example.net/root.json", <<"[[1, 2, 3]]">>, [{parser_fun, fun jsx:decode/1}]).
{ok,[[1,2,3]]}
Tools SHOULD take note of the URIs that schemas, including subschemas, provide for themselves using "id". This is known as "Internal referencing".
So it is recommended by IETF to support URIs both for schemas (which jesse
currently does) and subschemas (which is lacking).
We have an application which should be able to receive this type. We would like to handle this type as well.
A pull request has` been created: 0e07b0e
Please review and if you have any comments lets discuss it.
THX.
Json example:
{
"configs": {
"ba117f6-e26c-4dca-83e6-d397d90889d4": {
"cpu": "a"
}
}
}
Schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"configs": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$": {
"type": "object",
"properties": {
"cpu": {
"type": "string",
"enum": ["a", "b"]
}
}
}
}
}
}
}
enum
does not work as expected.
I have noticed that when putting regex patterns in pattern
and patternProperties
, \w
does not match unicode letters (like ū
, ス
, п
) as expected. You have to use [\p{L}\p{N}_]
instead. It looks like this is because the patterns are not compiled/executed with the ucp
in re:compile/2
:
ucp
Specifies that Unicode character properties are to be used when resolving \B, \b, \D, \d, \S, \s, \W and \w. Without this flag, only ISO Latin-1 properties are used. Using Unicode properties hurts performance, but is semantically correct when working with Unicode characters beyond the ISO Latin-1 range.
Later it explains:
By default, in
unicode
mode, characters with values > 255, that is, all characters outside the ISO Latin-1 character set, never match \d, \s, or \w, and always match \D, \S, and \W. These sequences retain their original meanings from before UTF support was available, mainly for efficiency reasons. However, if optionucp
is set, the behavior is changed so that Unicode properties are used to determine character types, as follows:
\d
Any character that\p{Nd}
matches (decimal digit)
\s
Any character that\p{Z}
or\h
or\v
\w
Any character that matches\p{L}
or\p{N}
matches, plus underscore
Is this intended to not use this behavior? It seems unexpected to me (having this behavior off by default is not generally expected). Obviously I have a workaround just using the pattern I mentioned ([\p{L}\p{N}_]
), so not a big deal if this isn't the behavior you all want.
It seems that validating an object with a list of 1000 objects with uniqueItems property takes time in the order of one second to complete.
This may be verified by creating an object such as:
{
"test": [
{"key1": "1", "key2": "1", "key3": "1", },
{"key1": "2", "key2": "2", "key3": "3", },
...
{"key1": "1000", "key2": "1000", "key3": "1000", },
]
}
and validating it against an schema such as:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"dataType": {
"type": "object",
"properties": {
"key1": "string",
"key2": "string",
"key3": "string"
}
}
},
"type": "object",
"properties": {
"test": {
"type": "array",
"uniqueItems": true,
"items": {
"$ref": "#/definitions/dataType"
}
}
}
}
I have a problem with "format": "date-time" validation and it seems like a bug.
Value: <<"2017-04-12T23:20:50Z">>
Property: #{<<"format">> => <<"date-time">>, <<"type">> => <<"string">>},
Brief analysis shows that problem most likely here: https://github.com/for-GET/jesse/blob/master/src/jesse_validator_draft4.erl#L956
Instead of "State", check_format function returns boolean.
check_format(Value, _Format = <<"date-time">>, State) when is_binary(Value) ->
try
<<Date:10/bytes, $T, Time:8/bytes, $Z>> = Value,
valid_date(Date) andalso valid_time(Time)
catch
error:{badmatch, _} -> handle_data_invalid(?wrong_format, Value, State)
end;
Hi, when checking the testing from upstream (from test/JSON-Schema-Test-Suite/
), I noticed that the tests that I think are used now are from 6 years ago, since the submodule commit has not changed.
Is this the case? Thanks.
Minimum schema:
{
"items": {
"required": ["b"]
},
"$schema": "http://json-schema.org/draft-04/schema#"
}
Minimum JSON
[{"c": "d"}]
jesse:validate_with_schema(#{<<"items">> => #{<<"required">> => [<<"b">>]},
<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>},
[#{<<"c">> => <<"d">>}]).
{ok,[#{<<"c">> => <<"d">>}]}
Expected: '{missing_required_property, "b"}', but validation succeeds instead.
Other implementations (eg http://jsonschemalint.com/draft4/) catches this error.
Since 1.5.0 or rather #54 this schema fails:
{
"refs": {
"not": {
"type": "null"
}
},
"$ref": "#refs/A"
}
with only_ref_allowed
. Not sure how correct the implementation of https://www.jsonschemavalidator.net is but it says this is a valid draft04 schema.
Side note: I believe jesse's handling of not
schemas is incorrect but I'll investigate more on that.
Hi, I am looking at https://hex.pm/packages/jesse/1.5.4 and it says there are 0 dependencies:
I'm not sure what flaw caused this to be published as such, but it should display both jsx
and rfc3339
(example: https://hex.pm/packages/erlcloud/3.2.18). Is there a chance that it was published with an outdated version of rebar3?
Hello from the JSON Schema spec project!
Does this project intend to add support for the recent drafts 6 and/or 7? I'm happy to answer any questions about the new (and forthcoming) drafts.
http://json-schema.org/specification.html
We will at some point be re-organizing the implementation lists to promote implementations supporting at least draft-06 and make draft-04-and-older-only implementations less visible.
It is really really really really really super opaque, frustrating and annoying to figure out how $ref
is supposed to work.
I think I'm too close to my emotions to write clearly and calmly about this, but I started a gist with some of my research notes. I'd eventually like to incorporate those notes in a cleaned up form into the documentation or README.
Is there an appetite to 👍 on a PR which simply attempts to see if the given $ref
key matches any records in ETS before all the canonicalization happens?
Hi. Would you consider a Git tag and publish to Hex.pm for master
? Thanks much.
Port of klarna/jesse#10 by @andreineculau
andreineculau 2 days ago
I personally would have liked 1) the error to be more uniform 2) the errors to be categorized e.g.
-type error_reason() :: { 'schema_invalid'
, Schema :: json_term()
, Error :: schema_error()
}
| { 'data_invalid'
, Schema :: json_term()
, Error :: data_error()
, Data :: json_term()
}.
-type schema_error() :: {schema_error_id(), Details :: any() }
-type schema_error_id() :: 'missing_id_field'
| ...
-type data_error() :: {data_error_id(), Details :: any() }
-type data_error_id() :: 'missing_required_property'
| ...
Reason: improved/clearer documentation IMO, and cleaner pattern matching.
Just a thought, @Cy6erBr4in :) Nice effort
PS: the above is just to illustrate the pattern; the type definition (for Details) can be more explicit per *errorid and more documentation-driven
I have file
{
"title" : "login",
"id" : "login.json#",
"$schema" : "http://json-schema.org/draft-04/schema#",
"type" : "object",
"properties" : {
"username" : {
"type" : "string",
"description" : "Username of the user"
},
"password" : {
"type" : "string",
"description" : "Plain password for user (security not defined as of now)"
}
},
"required" : ["username", "password"]
}
and another file
{
"title" : "Schema for session creation",
"$schema" : "http://json-schema.org/draft-04/schema#",
"id" : "session/put.json#",
"type" : "object",
"properties" : {
"content" : {
"type" : "object",
"oneOf" : [
{"$ref" : "login.json#"}
]
}
},
"required" : ["content"]
}
both the above schemas are loaded jesse, but the $ref attribute is not resolved and waits for infinite time. Am I doing anything wrong? Should I try using scheme_loader_fun option?
{
"title" : "Update Vehicle",
"$schema" : "http://json-schema.org/draft-04/schema#",
"id" : "post_vehicle.json",
"type" : "object",
"properties" : {
"content" : {
"type" : "object",
"anyOf" : [
{
"$ref" : "post_car.json"
},
{
"$ref" : "post_jet.json"
}
]
}
},
"required" : ["content"],
"additionalProperties" : false
}
The post_car.json has the below
{
"title" : "Update car",
"$schema" : "http://json-schema.org/draft-04/schema#",
"id" : "post_car.json",
"type" : "object",
"properties" : {
"steering_type" : {
"type" : "object",
"properties" : .......
}
...........
},
"additionalProperties" : false
}
The validation is failing with above and passing when I remove the below lines in post_car.json,
"additionalProperties" : false
if I am right if the schema validation fails in post_car.json, it should process for post_jet.json, this behaviour is happening only when the above property "addtionalProperties" is present.
Might be caused by fix for #22
Minimized example:
data.json
{
"unknown_field1":true,
"known_field1":{
"some_id":"410391-8351"
},
"known_field2":{
"client_ip":"127.0.0.1"
}
}
schema.json
{
"$schema": "http://json-schema.org/draft-04/schema#",
"additionalProperties": false,
"properties": {
"known_field2": {
"additionalProperties": false,
"properties": {
"client_ip": {
"type": "string",
"anyOf": [
{ "format": "ipv4" },
{ "format": "ipv6" }
]
}
}
},
"known_field1": {
"additionalProperties": false,
"properties": {
"some_id": {
"anyOf": [
{
"pattern": "^[0-9]{6}[+-]?[0-9]{3}[0-9A-Z]{1}$"
},
{
"pattern": "^[8-9][0-9]{8}$"
},
{
"pattern": "^([0-9]{8}|[0-9]{6})[+-]?[0-9]{4}$"
}
]
}
}
}
}
}
{ok, Data} = file:read_file("data.json"),
{ok, Schema} = file:read_file("schema.json"),
jesse:validate_with_schema(
Schema,
Data,
[{allowed_errors,infinity}, {parser_fun, fun jsx:decode/1}]).
%-----
{error,[{data_invalid,[<...>],
no_extra_properties_allowed,
[{<<"unknown_field1">>,true},
{<<"known_field1">>,[{<<"some_id">>,<<"4103918351">>}]},
{<<"known_field2">>,[{<<"client_ip">>,<<"127.0.0.1">>}]}],
[<<"unknown_field1">>]},
{data_invalid,[{<<"type">>,<<"string">>},
{<<"anyOf">>,
[[{<<"format">>,<<"ipv4">>}],[{<<"format">>,<<"ipv6">>}]]}],
any_schemas_not_valid,<<"127.0.0.1">>,
[<<"known_field2">>,<<"client_ip">>]},
{data_invalid,[{<<"anyOf">>,
[[{<<"pattern">>,<<"^[0-9]{6}[+-]?[0-9]{3}[0-9A-Z]{1}$">>}],
[{<<"pattern">>,<<"^[8-9][0-9]{8}$">>}],
[{<<"pattern">>,
<<"^([0-9]{8}|[0-9]{6})[+-]?[0-9]{4}$">>}]]}],
any_schemas_not_valid,<<"410321-9202">>,
[<<"known_field1">>,<<"some_id">>]}]}
I expect it to only generate no_extra_properties_allowed
, but it also generates any_schemas_not_valid
Would you be interested in a pull request that moves CI from Travis CI to GitHub Actions, or at least adds GitHub Actions alongside Travis CI? It seems travis-ci.org will stop being useful from Dec 31 2020. I can do it, except that I think you need to create the workflow yml, first, in master
, after which I can branch off of.
Last night (8th jan 2017) we did a build which clones the jesse repo and we pulled the latest master branch, which seems to be broken. From a quick review the order of parameters from jesse:add_schema/2 (which we call) into jesse_database seem to be incorrect, though I'm judging by variable name only, with no knowledge of internal workings.
Last fridays (6th jan 2017) master version was working fine.
jesse build will fail on some configuration using erlang OTP 21. Having a release including the last commit would work.
With jesse 1.4.0, I'm having trouble using the options []
and [{allowed_errors, 1}]
, because yields different results. For example, consider the following example document:
{
"data": {
"type": "document",
"id": 1
}
}
where /data/id
should be "1"
and not 1
and the schema from http://jsonapi.org/schema
The problem is that using []
or [{allowed_errors, 0}]
yields a different error than [{allowed_errors, 1}]
. Because of that there is no single API call that would always yield the same error first (i.e. one has to first validate and get an error, then validate again with allowed errors set to 1
to get the specific error).
Maybe I'm misunderstanding how jesse operates, but for the above document and schema using infinity
yields a lot of errors for all the branches of the schema (although the document is correct).
Is there a canonical way to validate the document successfully (because it is valid) and get the most specific error first?
Using [{allowed_errors, 1}]
we get:
34> jesse:validate(jsonapi, #{<<"data">> => #{<<"type">> => <<"user">>, <<"id">> => 1}}).
{error,[{data_invalid,#{<<"type">> => <<"string">>},
wrong_type,1,
[<<"data">>,<<"id">>]}]}
Using no options, we get not_one_schema_valid
as the first error, where one would expect the most specific error first (as with allowing only 1 error).
1> jesse:validate(jsonapi, #{<<"data">> => #{<<"type">> => <<"user">>, <<"id">> => 1}}).
{error,[{data_invalid,#{...schema redacted...},
not_one_schema_valid,
#{<<"data">> => #{<<"id">> => 1,<<"type">> => <<"user">>}},
[]}]}
see #98 (comment)
to clarify: we should be able to test with any desired erlang version, including those disabled in dfe9348#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03f
FWIW I see kerl supports only 20+ https://github.com/kerl/kerl#otp-support-policy
I don't know if this is legacy (jesse not supporting $ref in the beginning) but i have a commit that adds loading (and adding to internal storage) referenced schemas given a start schema with an id using the file: or http: URI scheme.
This works brilliantly for the CLI version.
Ping @Cy6erBr4in
I have my schemas in priv/schema
which I am loading by
Path = code:application(foo) ++ "/schema",
jesse:load_schemas(Path, fun(D) -> jiffy:decode(D, [return_maps]) end).
Later, I want to reference these loaded schemas by the ids embedded in the schemas themselves, but it appears the only way they're stored is using absolute paths on my local disk.
Example:
{
"$id": "http://example.com/schema/foo.json",
}
Later I want to
jesse:validate("http://example.com/schema/foo.json", InputJSON)
but of course it says there's no schema named that. It seems like jesse attempts to locate the id attribute in a schema - does it just ignore it?
Schema = jiffy:decode(F, [return_maps]).
#{<<"additionalProperties">> => false,
<<"minProperties">> => 1,
<<"patternProperties">> =>
#{<<"^[a-zA-Z]{1}[a-zA-Z0-9_]{2,}$">> =>
#{<<"oneOf">> =>
[#{<<"type">> => <<"number">>},
#{<<"type">> => <<"string">>},
#{<<"type">> => <<"boolean">>},
#{<<"type">> => <<"null">>},
#{<<"items">> =>
#{<<"oneOf">> =>
[#{<<"type">> => <<"number">>},
#{<<"type">> => <<"string">>},
#{<<"type">> => <<"boolean">>}]},
<<"type">> => <<"array">>}]}}}
27> Body1 = #{ <<"key1">> => 9, <<"albatross">> => <<"value1">>, <<"list">> => [true, 1, [<<"something">>, null]]}.
#{<<"albatross">> => <<"value1">>,<<"key1">> => 9,
<<"list">> => [true,1,[<<"something">>,null]]}
Expected result: a list value with a list inside of it should fail oneOf
validation as defined above.
Actual result:
28> jesse:validate_with_schema(Schema, Body1).
{ok,#{<<"albatross">> => <<"value1">>,<<"key1">> => 9,
<<"list">> => [true,1,[<<"something">>,null]]}}
I don't understand why?
I am the developer of ejsv. I needed a JSON schema validation library that could easily be extended and shared logic where possible, and I read through Jesse and found it very difficult in that regard. I am not familar with JSON schema, but I like my Erlang work 🤣
So I managed to achieve the requirement by implementing an abstraction between the keyword rules and the assertions themselves, but I failed to think about $ref's 💩
I was thinking I needed a more fine grained API in ejsv_cache
, what might you recommend? The code is quite simple to read through (I hope) and I would really appreciate your thoughts otherwise!
Clarifying up the side note I had in #62:
To specify which validator to use by default (if there's no $schema property in
the given schema), one should use 'default_schema_ver' option when call
`jesse:validate/3` or `jesse:validate_with_schema/3`, the value should be
a binary consisting a schema path,
i.e. <<"http://json-schema.org/draft-03/schema#">>.
When $schema
is specified, the value of default_schema_ver
is used regardless.
not
is valid in draft3 but not draft4.
jesse:validate_with_schema(#{<<"$schema">> => <<"http://json-schema.org/draft-03/schema#">>, <<"components">> => #{<<"schemas">> => #{<<"A">> => #{<<"not">> => #{<<"type">> => <<"null">>}}}}, <<"$ref">> => <<"#/components/schemas/A">>}, <<"wef">>, [{allowed_errors,infinity}, {default_schema_ver,<<"http://json-schema.org/draft-04/schema#">>}]).
{error,[{data_invalid,#{<<"not">> =>
#{<<"type">> => <<"null">>}},
not_schema_valid,<<"wef">>,[]}]}
With default set to 03 instead of 04 the not
is correctly checked:
jesse:validate_with_schema(#{<<"$schema">> => <<"http://json-schema.org/draft-03/schema#">>, <<"components">> => #{<<"schemas">> => #{<<"A">> => #{<<"not">> => #{<<"type">> => <<"null">>}}}}, <<"$ref">> => <<"#/components/schemas/A">>}, <<"wef">>, [{allowed_errors,infinity}, {default_schema_ver,<<"http://json-schema.org/draft-03/schema#">>}]).
{ok,<<"wef">>}
Hello!
Trying to validate this json:
{
"attachments": [
{
"mimetype": "image/jpeg",
"content": "R0lGODlhGQAZAPMHAAAAAFpaWv+gAN+MNdDWDfj/AH1t+P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAgALAAAAAAZABkAAASKEMlJq7046wvA3oDgfVwoklUnrMGJIia7jmS8zgFqFzdA5B+b4LAa+GgYIYA4MBKQpZagsGwOCE9JAGjxFb6FQGEgzopz267322Frw2dLq9P+BjxScfhyJ4C3Jy0texkdUgIGRjcsGgFYAAYGAE0maRmCj5OUlo1bWz8BZFwvgAKcLxOnqKusrRsRADs=",
"filename": "chili-pepper.jpg"
}
],
"inlines": [
{
"mimetype": "image/jpeg",
"filename": "chili-pepper-inline1.jpg"
}
],
"category": "wgnc:exclusive",
"body_html": "<HTML><body><img src=\"cid:chili-pepper-inline1.jpg\"></body></HTML>",
"text": "test_text",
"subject": "Autotest subject"
}
with this schema:
{
"description": "Email message",
"type": "object",
"properties": {
"subject": {
"type": "string",
"minLength": 1,
"pattern": "^[^\\r\\n\\t]+$"
},
"text": {
"type": "string",
"minLength": 1
},
"html": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"attachments": {
"type": "array",
"items": {
"type": "object",
"required": [
"filename",
"mimetype",
"content"
],
"properties": {
"filename": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"mimetype": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"content": {
"type": "string",
"minLength": 1,
"maxLength": 25000000,
"pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$"
}
}
}
},
"inlines": {
"type": "array",
"items": {
"type": "object",
"required": [
"filename",
"mimetype",
"content"
],
"properties": {
"filename": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"mimetype": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"content": {
"type": "string",
"minLength": 1,
"maxLength": 25000000
}
}
}
}
},
"anyOf": [
{
"required": [
"subject",
"html"
]
},
{
"required": [
"subject",
"text"
]
}
]
}
result is:
./_build/default/bin/jesse --json /Users/a_dziaineka/Repos/jesse/test_json/email_message.json -- /Users/a_dziaineka/Repos/jesse/test_json/test.json
{"filename":"/Users/a_dziaineka/Repos/jesse/test_json/test.json","result":"ok"}
But online validator found error (link to the case):
Message:
Required properties are missing from object: content.
Schema path:
#/properties/inlines/items/required
Is it a bug?
When allowed_errors
option is set, oneOf
and anyOf
doesn't work.
You get an error back from the case that didn't match in the oneOf
, which is not really an error, but the expected behaviour.
Here is the ct
case I'm using.
jesse_one_of_issue(_Config) ->
Schema = #{
<<"$schema">> => <<"http://json-schema.org/draft-04/schema#">>,
<<"type">> => <<"string">>,
<<"oneOf">> => [
#{<<"enum">> => [<<"my_enum">>]},
#{<<"pattern">> => <<"^x-[a-z0-9\\-\\.]+$">>}
]
},
jesse:add_schema(test, Schema),
% works
GoodOpts = [],
{ok, _} = jesse:validate(test, <<"my_enum">>, GoodOpts),
{ok, _} = jesse:validate(test, <<"x-foo">>, GoodOpts),
{error, _} = jesse:validate(test, <<"foo">>, GoodOpts),
{error, _} = jesse:validate(test, <<"1-x">>, GoodOpts),
% doesn't work
BadOpts = [{allowed_errors, infinity}],
{ok, _} = jesse:validate(test, <<"my_enum">>, BadOpts),
{ok, _} = jesse:validate(test, <<"x-foo">>, BadOpts),
{error, _} = jesse:validate(test, <<"foo">>, BadOpts),
{error, _} = jesse:validate(test, <<"1-x">>, BadOpts).
Hi,
I have a local id in file "test/test1.json" and how can I reference this file in another file somethgn like below?
"$ref" : "test/test1.json"
Hi,
The dependency rfc3339
was introduced to implement date-time
format validation in Draft 4 schemas. Unfortunately [for me] another RFC 3339 library with the same module name is used extensively through my application:
jesse:validate_with_schema(#{<<"type">> => <<"string">>, <<"format">> => <<"date-time">>}, <<"1970-01-01T00:00:00Z">>, [{default_schema_ver, <<"http://json-schema.org/draft-04/schema#">>}]).
** exception error: undefined function rfc3339:parse/1
in function jesse_validator_draft4:valid_datetime/1 (/home/me/proj/myapp/_build/default/lib/jesse/src/jesse_validator_draft4.erl, line 1349)
in call from jesse_validator_draft4:check_format/3 (/home/me/proj/myapp/_build/default/lib/jesse/src/jesse_validator_draft4.erl, line 949)
in call from jesse_validator_draft4:check_value/3 (/home/me/proj/myapp/_build/default/lib/jesse/src/jesse_validator_draft4.erl, line 217)
in call from jesse_schema_validator:validate/3 (/home/me/proj/myapp/_build/default/lib/jesse/src/jesse_schema_validator.erl, line 45)
in call from jesse:validate_with_schema/3 (/home/me/proj/myapp/_build/default/lib/jesse/src/jesse.erl, line 180)
If the issue isn't a «wontfix» one, there may be a few ways to fix it:
date-time
format validation to Draft 3 module, where due to more strict date-time
format it can be reasonable implemented without any dependencies right in the jesse
.date-time
format validation, allow user to store custom format validators in jesse
database, suggest user to implement custom date-time
format validator any way he finds suitable.date-time
format validation.I am dynamically generating some JSON validation, but certain properties are not being validated against. Here's an example:
Candidates = Election#election.candidates,
Validation = {[{<<"type">>, <<"object">>},
{<<"properties">>,
{[{Candidate, {[{<<"type">>, <<"integer">>}]}} || Candidate <- Candidates]}},
{<<"required">>, Candidates},
{<<"additionalProperties">>, false}
]},
io:fwrite("Schema: ~s~n", [jiffy:encode(Validation)]),
io:fwrite("~p~n", [Validation]),
jesse:validate_with_schema(Validation, jiffy:decode(Body)).
And here's the output, along with a report of what was passed in if it passes validation:
Schema: {"type":"object","properties":{"x":{"type":"integer"},"y":{"type":"integer"},"z":{"type":"integer"}},"required":["x","y","z"],"additionalProperties":false}
{[{<<"type">>,<<"object">>},
{<<"properties">>,
{[{<<"x">>,{[{<<"type">>,<<"integer">>}]}},
{<<"y">>,{[{<<"type">>,<<"integer">>}]}},
{<<"z">>,{[{<<"type">>,<<"integer">>}]}}]}},
{<<"required">>,[<<"x">>,<<"y">>,<<"z">>]},
{<<"additionalProperties">>,false}]}
Passed validation: {[{<<"x">>,1}]}
Passed validation: {"x":1}
As you can see, when an object containing only one of the three required properties is passed in, it passes validation.
On the other hand, if I pass in {"i":1}
, the validation fails according to the additionalProperties
constraint.
Can you let me know what i'm doing wrong?
According to specification for the function jesse_state:find_schema/2 expected that type of SchemaURI is binary. Actually it always list (is used re:split)
When the JSON schema uses oneOf
, anyOf
or similar and the validation fails, the error message returned is not very helpful for the user. For example, in the case of oneOf
the error is only not_one_schema_valid
.
From what I've seen in the code, it should be possible to check all of the schemas in the specification provided in anyOf
/oneOf
, and add the errors from the schema with the least number of errors.
I am interested in implementing this change if you are willing to merge the PR 😄.
When validating an object against a schema with multiple fields in the required
array (draft4), it would be great to get back either:
{missing_required_property, [<<"foo">>, <<"bar">>]}
{missing_required_property, Key}
It looks straightforward enough in the code, something like (untested):
check_required(Value, [_|_]=Required, State) ->
{_, Missing} = lists:partition(fun(Property) -> get_value(PropertyName, Value) =/= ?not_found end, Required),
case Missing of
[] -> State;
MissingKeys -> %% do appropriate error return
end;
Thoughts? Happy to submit the PR if this is a worthwhile change.
https://travis-ci.org/for-GET/jesse/jobs/95224229
jesse_error.erl:87: Function handle_data_invalid/3 has no local return
jesse_error.erl:100: Function handle_schema_invalid/2 has no local return
jesse_error.erl:109: Function handle_error/2 has no local return
jesse_error.erl:113: Fun application will fail since ErrorHandler :: fun(({'state',_,_,'undefined' | [binary()],'infinity' | 'undefined' | non_neg_integer(),'undefined' | [any()],'undefined' | fun(({'state',_,_,'undefined' | [binary()],'infinity' | 'undefined' | non_neg_integer(),'undefined' | [any()],'undefined' | fun(({'state',_,_,'undefined' | [binary()],'infinity' | 'undefined' | non_neg_integer(),'undefined' | [any()],'undefined' | fun((_) -> [any()]),atom()}) -> [any()]),atom()}) -> [any()]),atom()}) -> [any()]) is not a function of arity 3
jesse_state.erl:84: Invalid type specification for function jesse_state:get_default_schema_ver/1. The success typing is (#state{current_path::'undefined' | [binary()],allowed_errors::'infinity' | 'undefined' | non_neg_integer(),error_list::'undefined' | [any()],error_handler::'undefined' | fun((#state{current_path::'undefined' | [any()],allowed_errors::'infinity' | 'undefined' | non_neg_integer(),error_list::'undefined' | [any()],error_handler::'undefined' | fun((_) -> any()),default_schema_ver::atom()}) -> [any()]),default_schema_ver::atom()} | jesse_state:state()) -> atom()
jesse_validator_draft3.erl:304: Function wrong_type/2 has no local return
jesse_validator_draft3.erl:405: The created fun has no local return
jesse_validator_draft3.erl:405: The call lists:foldl(fun((_,_) -> none()),State::jesse_state:state(),Extras::[{atom() | binary() | [any()],_},...]) will never return since the success typing arguments are (fun((_,_) -> any()),any(),[any()])
jesse_validator_draft3.erl:492: The created fun has no local return
jesse_validator_draft3.erl:494: The call jesse_validator_draft3:check_value(Index::number(),Item::any(),Items::any(),CurrentState::any()) will never return since it differs in the 1st argument from the success typing arguments: (binary(),any(),any(),jesse_state:state())
jesse_validator_draft3.erl:541: The created fun has no local return
jesse_validator_draft3.erl:546: The call jesse_validator_draft3:check_value(Index::number(),Item::any(),Schema::any(),NewState::jesse_state:state()) will never return since it differs in the 1st argument from the success typing arguments: (binary(),any(),any(),jesse_state:state())
jesse_validator_draft3.erl:848: The call jesse_validator_draft3:check_value('extends',Value::any(),Extends::any(),jesse_state:state()) will never return since it differs in the 1st argument from the success typing arguments: (binary(),any(),any(),jesse_state:state())
jesse_validator_draft3.erl:939: Function handle_data_invalid/3 has no local return
jesse_validator_draft3.erl:943: Function handle_schema_invalid/2 has no local return
jesse_validator_draft3.erl:963: Function remove_last_from_path/1 will never be called
In jesse 1.4.0, an invalid schema that results in an {schema_invalid, Schema, schema_invalid}
error and the option {allowed_errors, infinity}
causes an infinite loop. Any other value N for allowed_errors
causes N copies of the schema_invalid
error.
It will be great, if we can validate map()
JSON representation (at least map data, not map schema).
jiffy:decode(<<...>>, return_maps).
I've already made pull-request to kvc with maps support etrepum/kvc#9 (not merged yet). But changes may be ported right now. (Why we use our own copy of kvc instead of rebar dep?).
Also, I'm ready to implement this myself, if someone is interested to review it.
Hey,
We are updating jesse internally and would like to use upstream version numbers. Any chance we could get a tagged release of the current version?
SUBJ?
1683 was green: https://travis-ci.org/github/for-GET/jesse/builds/749489608
1684 was red: https://travis-ci.org/github/for-GET/jesse/builds/750746033
@paulo-ferraz-oliveira would you be so kind to have a look? I see you're active there.
I'm locking it to 0.2.0 for now. Should probably be locked anyway.
# src/jesse.erl [FAIL] 192Error: 'undef' while applying rule 'no_trailing_whitespace'. 193Error: 'undef' while applying rule 'no_tabs'. 194Error: 'undef' while applying rule 'line_length'. 195# src/jesse_cli.erl [FAIL] 196Error: 'undef' while applying rule 'no_trailing_whitespace'. 197Error: 'undef' while applying rule 'no_tabs'. 198Error: 'undef' while applying rule 'line_length'. 199# src/jesse_database.erl [FAIL] 200Error: 'undef' while applying rule 'no_trailing_whitespace'. 201Error: 'undef' while applying rule 'no_tabs'. 202Error: 'undef' while applying rule 'line_length'. 203# src/jesse_error.erl [FAIL] 204Error: 'undef' while applying rule 'no_trailing_whitespace'. 205Error: 'undef' while applying rule 'no_tabs'. 206Error: 'undef' while applying rule 'line_length'. 207# src/jesse_json_path.erl [FAIL] 208 - macro_names 209 - The macro named "Exp" on line 19 does not respect the format defined by the regular expression '"^([A-Z][A-Z_0-9]+)$"'. 210Error: 'undef' while applying rule 'no_trailing_whitespace'. 211Error: 'undef' while applying rule 'no_tabs'. 212Error: 'undef' while applying rule 'line_length'. 213# src/jesse_lib.erl [FAIL] 214Error: 'undef' while applying rule 'no_trailing_whitespace'. 215Error: 'undef' while applying rule 'no_tabs'. 216Error: 'undef' while applying rule 'line_length'. 217# src/jesse_schema_validator.erl [FAIL] 218Error: 'undef' while applying rule 'no_trailing_whitespace'. 219Error: 'undef' while applying rule 'no_tabs'. 220Error: 'undef' while applying rule 'line_length'. 221# src/jesse_state.erl [FAIL] 222Error: 'undef' while applying rule 'no_trailing_whitespace'. 223Error: 'undef' while applying rule 'no_tabs'. 224Error: 'undef' while applying rule 'line_length'. 225# src/jesse_tests_util.erl [FAIL] 226Error: 'undef' while applying rule 'no_trailing_whitespace'. 227Error: 'undef' while applying rule 'no_tabs'. 228Error: 'undef' while applying rule 'line_length'. 229# src/jesse_validator_draft3.erl [FAIL] 230Error: 'undef' while applying rule 'no_trailing_whitespace'. 231Error: 'undef' while applying rule 'no_tabs'. 232Error: 'undef' while applying rule 'line_length'. 233# src/jesse_validator_draft4.erl [FAIL] 234Error: 'undef' while applying rule 'no_trailing_whitespace'. 235Error: 'undef' while applying rule 'no_tabs'. 236Error: 'undef' while applying rule 'line_length'. 237# test/jesse_json_path_tests.erl [FAIL] 238Error: 'undef' while applying rule 'no_trailing_whitespace'. 239Error: 'undef' while applying rule 'no_tabs'. 240Error: 'undef' while applying rule 'line_length'. 241# test/jesse_schema_validator_tests.erl [FAIL] 242Error: 'undef' while applying rule 'no_trailing_whitespace'. 243Error: 'undef' while applying rule 'no_tabs'. 244Error: 'undef' while applying rule 'line_length'. 245# test/jesse_tests_draft3_SUITE.erl [FAIL] 246Error: 'undef' while applying rule 'no_trailing_whitespace'. 247Error: 'undef' while applying rule 'no_tabs'. 248Error: 'undef' while applying rule 'line_length'. 249# test/jesse_tests_draft4_SUITE.erl [FAIL] 250Error: 'undef' while applying rule 'no_trailing_whitespace'. 251Error: 'undef' while applying rule 'no_tabs'. 252Error: 'undef' while applying rule 'line_length'. 253===> Linting failed 254
I was looking at jesse's performance, with one or two dozen of schemas that I added with more or less arbitrary keys via jesse:add_schema("some key", SchemaData, [{parser_fun, fun jsx:decode/1}]).
When doing validation I found that (at least on my MacOS box with SSD), about 30% of the run-time is used in treating my arbitrary key as a relative file name. I found it's was a bit better on some other machines/OSes, but jesse_state:canonical_path consistently used a large part of the run time in my tests. Just making my "arbitrary keys" start with "http://" (so canonical_path uses string functions instead of filename functions) gave me a 30% speedup (run time for a single pass of my little test suite dropped from roughly 160 ms to 110 ms).
IMHO implicitly treating such arbitrary keys (that don't start with "file://") as files comes as a surprise to the user of jesse. Why not have that last clause of canonical_path just return the arbitrary key that was given to it, instead of forcibly treating it as a file name and "upgrading" it to a "file://" URI? Having to explicitly create an invalid URI like "http://some.name" and use that as a key, just to avoid that runtime penalty, feels really strange.
And then you just released 1.5.0, so of course I was curious about how that would affect performance. After a quick glance at the source code, I didn't have much hope that I would be freed from the need to prefix my keys with "http://" - and indeed, I wasn't.
Much worse, compared to the git version I used up to now (bd1c7e5), runtimes roughly doubled. I.e. I did all this profiling and testing to get the runtime of my testsuite down from 160 ms to 110 ms and after updating to the latest jesse release I'm now at 210 ms (and if I remove that hack with the "http://" prefix for testing, it's way over 300 ms). After doing some profiling on 1.5.0, I can't really point to a specific place in the code (like I could with jesse_state:canonical_path) and say that's where you are using up all that additional time, so for now I can only report those results to you and hope that maybe you can figure out how to speed things up again ...
Since you are possibly wondering why anyone would care about jesse's performance: Think of e.g. a cowboy server which receives JSON requests from clients, validates these requests and replies either with ok (and starts a job on a backend server) or with an error message about an invalid JSON. In that scenario jesse is really consuming a large chunk of the runtime on that frontend. And since I'm hoping for lots and lots of clients, jesse validation really becomes a bottleneck during traffic spikes.
Let's say I have a "common" schema with common data types for a few different schemas.
{
"$id": "http://example.com/schema/common.json",
"definitions": {
"account_id": {
"$id": "#account_id",
"description": "A valid customer account id",
"type": "integer"
},
"media-type": {
"$id": "#media_type",
"description": "A MIME media type",
"type": "string"
}
}
}
And then I have another schema file
{
"$id": "http://example.com/schema/customer-data.json",
"properties": {
"account_id": { "$ref": "common.json#account_id" },
"media_type": { "$ref": "common.json#media-type" }
}
}
When I try this, I get
{error,[{schema_invalid,#{<<"$ref">> => <<"common.json#account_id">>},
{schema_not_found,"file:///Users/mallen/sandbox/_build/test1/lib/test1/priv/schema/common.json#account_id"}}]}
How can I get this to resolve?
Port of klarna/jesse#38 by @Licenser
Hi, I was thinking it'd be really nice to have a pre-build function that formats the erlang error into a json error to return to the sender.
It probably can be done easily in the application using jesse but it seems such a common thing that I feel like it could be included directly?
-spec error_to_json([jesse:error()]) -> binary().
%% ...
something like that?
Working example:
ok = jesse:add_schema("./schema3", #{
<<"id">> => <<"schema3">>,
<<"type">> => <<"object">>,
<<"properties">> => #{
<<"key3">> => #{
<<"type">> => <<"string">>
}
}
}).
ok = jesse:add_schema("./schema2", #{
<<"id">> => <<"schema2">>,
<<"type">> => <<"object">>,
<<"properties">> => #{
<<"key2">> => #{
<<"allOf">> => [
#{<<"$ref">> => <<"schema3">>}
]
}
}
}).
ok = jesse:add_schema("./schema1", #{
<<"id">> => <<"schema1">>,
<<"properties">> => #{
<<"key1">> => #{
<<"allOf">> => [
#{<<"$ref">> => <<"schema2">>}
]
}
}
}).
{ok, _} = jesse:validate("./schema1", #{<<"key1">> => #{<<"key2">> => #{<<"key3">> => <<"123">>}}}, [
{default_schema_ver, <<"http://json-schema.org/draft-04/schema#">>}
]).
Problems:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.