I have following java Object:
ProcessBean.java
import java.util.List;
import com.fasterxml.jackson.annotation.JsonRootName;
#JsonRootName(value = "process")
public class ProcessBean{
private Integer id;
private String name;
private String description;
private String user;
private String executePermissions;
private String createdDtm;
private String updatedDtm;
private Process tpProcess;
private List<ProcessParamBean> processParameters;
/* --------Getters and Setters ----------*/
}
I need to find the JSON schema for this object which is used to display these fields on UI. The order of fields in UI is determined by the order of properties in JSON schema generated.
I have generated the schema using following code:
DataSpec.java
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
public class DataSpec {
public static <T> String getDataSpec(Class<T> clazz) {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
MetauiVisitorContext obj = new MetauiVisitorContext();
visitor.setVisitorContext(obj);
try {
mapper.acceptJsonFormatVisitor(clazz, visitor);
JsonSchema schema = visitor.finalSchema();
return mapper.writeValueAsString(schema);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
}
MetauiVisitorContext.java
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.module.jsonSchema.factories.VisitorContext;
public class MetauiVisitorContext extends VisitorContext{
#Override
public String getSeenSchemaUri(JavaType aSeenSchema) {
return null;
}
}
The resultant schema looks like below:
{
"type": "object",
"id": "urn:jsonschema:com:restservices:api:jsonbeans:ProcessBean",
"properties": {
"updatedDtm": {
"type": "string"
},
"createdDtm": {
"type": "string"
},
"name": {
"type": "string"
},
"tpProcess": {
"type": "object",
"id": "urn:jsonschema:com:restservices:api:jsonbeans:Process",
"properties": {
"name": {
"type": "string"
},
"id": {
"type": "integer"
}
}
},
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"processParameters": {
"type": "array",
"items": {
"type": "object",
"id": "urn:jsonschema:com:restservices:api:jsonbeans:ProcessParamBean",
"properties": {
"updatedDtm": {
"type": "string"
},
"defaultValue": {
"type": "string"
},
"createdDtm": {
"type": "string"
},
"masterParam": {
"type": "object",
"id": "urn:jsonschema:com:restservices:api:jsonbeans:MasterParamBean",
"properties": {
"updatedDtm": {
"type": "string"
},
"createdDtm": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"id": {
"type": "integer"
}
}
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"processParamId": {
"type": "integer"
},
"id": {
"type": "integer"
},
"userPrompted": {
"type": "boolean"
},
"tags": {
"type": "string"
}
}
}
},
"executePermissions": {
"type": "string"
},
"user": {
"type": "string"
}
}
}
As it can be seen that the order of properties in JSON schema does not match the order of fields defined in Java object which is necessary in my case.
So how can I determine the sequence of the properties?
There is not an intrinsic order of properties in a JSON schema object, as it happens with a regular JSON or javascript object. Indeed, it does not change the validation semantics. The following schemas validate exactly the same collection of objects:
Schema 1:
{
"properties" : {
"prop1" : {
"type" : "string"
},
"prop2" : {
"type" : "number"
}
}
}
Schema 2:
{
"properties" : {
"prop2" : {
"type" : "number"
},
"prop1" : {
"type" : "string"
}
}
}
If you want to preserve some ordering you need to do it within and array. You could achieve this by using an array of objects instead of an object in the items clause. An example:
{
"type" : "array" :
"items" : [{
"properties" : {
"prop2" : {
"type" : "number"
}
}
}, {
"properties" : {
"prop1" : {
"type" : "string"
}
}
}
]
}
This way you get an ordered definition of UI items. Unfortunately, it forces you to nest each single property in an object.
Order is out of scope for json-schema. For that you need to look into json-forms https://jsonforms.io/, standards, which is extension of json-schema. It consist of three separate JSON data :
schema
values
ui-schema
The ui-schema contains the information about the ui and presentation, like order of properties (fields) and the their visual presentation, like "hidden", "read-only".
Related
I am trying to create JSON schema for below JSON,
{
"messages":{
"bookCreated":{
"default":{
"channels":{
"sqs":{
"enabled":true,
"topic":"sample-topic",
"shares":true
}
}
}
},
"bookCreationInProgress":{
"default":{
"channels":{
"sns":{
"enabled":true,
"topic":"sample-sns",
"shares":true
}
}
}
},
"bookCreationCompleted":{
"default":{
"channels":{
"s3":{
"enabled":true,
"topic":"sample-s3-bucket",
"shares":true
}
}
}
}
}
}
Inside message bookCreated, bookCreationInProgress , bookCreationCompleted similarly we have several dynamic properties. Inside each of these objects default and channel details are mandatory.
And each channel has a set of mandatory attributes.
I browsed internet to create JSON schema for the above json but I couldn't get any reference of how to create json schema for nested map objects.
Since I couldn't able to construct the json schema for very first dynamic object I couldn't able to construct the schema further.
{
"$schema": "app_messages",
"type": "object",
"additionalProperties": true,
"anyOf": [
{
"required": ["messages"]
}
],
"properties": {
"id": {
"type": "string"
}
}
}
It would be really great if somebody would help me to share the pointers of how to handle map of dynamic properties in JSON schema. Any help would be really appreciable.
A good solution use "additionalProperties" as json-object (instead of boolean) combined to "$ref" and "$defs".
A talking example might be:
{
"$schema": "app_messages",
"type": "object",
"additionalProperties": {
"anyOf": [
{ "$ref": "#/$defs/subobj1" },
{ "$ref": "#/$defs/subobj2" }
]
},
"properties": {
"id": { "type": "string" }
},
"$defs": {
"subobj1": {
"type": "object",
"properties": {
"message": { "type": "string" }
...
},
"required": [ "message" ]
},
"subobj2": {
"type": "object",
"properties": {
"message": { "type": "string" }
...
},
"required": [ "message" ]
}
}
}
In this way main object can have "id" properties and all additional properties must match one of sub definition "subobj1" or "subobj2".
enter image description hereI have a json schema looks like below,and I want load the definitions into D and E based on the values of B and C for that I've written allOf conditioning.
and i'm using json-schema-validator for json schema validation in application.
i)the below schema always passing as valid because the allOf condition never evaluated and it's not
loading validators properties like maxLenth,multipleOf from the definitions.
ii)I was suspecting I did the conditioning in a wrong place(the root schema or sub schema) and i tried
moving this allof logic to subschema level(inside the B,C and D,E)
iii)I've tried executing the allOf example mentioned on https://json-schema.org/understanding-json-schema/reference/conditionals.html it is also passing as valid. for this I did verified on a online josn schema validator http://json-schema-validator.herokuapp.com/ which is also using same library json-schema-validator.
iv)is there any ValidationConfiguration requires for JsonSchemaFactory to validate the Draft7 jsonSchema conditioning since the Defaultlibrary is DRAFT-4 on this json-schema-validator.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"A",
"B",
"C",
"D",
"E"
],
"properties": {
"A": {
"type": "string"
},
"B": {
"type": "string",
"enum": ["TEST1","TEST2"]
},
"C": {
"type": "string",
"enum": ["TEST3","TEST4"]
},
"D": {
"type": "object"
},
"E": {
"type": "object"
}
},
"allOf": [
{
"if": {
"properties": { "B": { "const": "TEST1" } }
},
"then": {
"properties": { "D": { "$ref": "#/definitions/test" } }
}
},
{
"if": {
"properties": { "B": { "const": "TEST2" } }
},
"then": {
"properties": { "D": { "$ref": "#/definitions/testTwo" } }
}
},
{
"if": {
"properties": { "C": { "const": "TEST3" } }
},
"then": {
"properties": { "E": { "$ref": "#/definitions/testThree" } }
}
},
{
"if": {
"properties": { "C": { "const": "TEST4" } }
},
"then": {
"properties": { "E": { "$ref": "#/definitions/test4" } }
}
}
],
"definitions": {
"testOne":{"type":"object"},
"testTwo":{"type":"object"},
"testThree":{"type":"object"},
"testFour":{"type":"object"}
}
}
And the javaCode looks like
#PostMapping("/sendMessage")
public ProcessingReport sendMessage(#RequestBody SampleRequest request) throws IOException, ProcessingException {
//step-1 writing request object into String
String requestJson = objectMapper.writeValueAsString(request);
//Step-2 getting jsonNode for requested json
JsonNode dataNode = JsonLoader.fromString(requestJson);
//step -3 creating jsonSchema factory(default)
JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
//validating requested jsonNode(dataNode) against SchemaNode(schema of request json,which is loaded from resources)
ProcessingReport report = factory.getJsonSchema(schemaNode).validate(dataNode);
//Processing report resulting the given json validation is successful or not
if(!report.isSuccess()) {
System.out.println(report);
}
return report;
}
json-schema-validator only supports draft-03 and draft-04. if/then/const were added in later drafts. Those keywords get ignored resulting in the no-op behavior you're experiencing.
You have two choices
pick a different implementation that supports draft-07
Use the Implication Pattern instead. It's a little more verbose, but the result is the same.
I am new to this API. Trying to generate a class of 10-15 fields of different data type. But generated class has the 1st variable of the type I declared but remaining if type Object as below.
// Schema
{
"type":"object",
"properties": {
"foo": {
"type": "string"
},
"bar": {
"type": "String"
},
"baz": {
"type": "String"
}
}
}
//Generated Class
//...
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"foo",
"bar",
"baz"
})
public class Sample{
#JsonProperty("foo")
private String foo;
#JsonProperty("bar")
private Object bar;
#JsonProperty("baz")
private Object baz;
#JsonIgnore
....//
If you notice the 2nd and 3rd variable are declared String but result generated from class is of type object. Can someone help understand what the issue is?
{
"type":"object",
"properties": {
"length": {
"type": "string"
},
"width": {
"type": "string"
},
"height": {
"type": "string"
},
"dimensionalWeight": {
"type": "string"
}
}
}
I have a Model 'NewModel' with the following property
import com.fasterxml.jackson.databind.JsonNode;
#ApiModel(
description = "Data Class to hold new details"
)
public class NewModel {
#ApiModelProperty(
notes = "Value in JSON key:value format. Can be any key:value pair",
example = "{ds:2017:08:05,hh:11}"
)
private final JsonNode value;
(... getters and setters ...)
}
Apart from this, I have some rest controllers which get a JSON in request body. I use this model to get the JSOn from request body.
I have configured springfox swagger using maven, and generated the api definition. But in the generated API definitions, this model has been generated as
"NewModel": {
"type": "object",
"properties": {
"value": {
"example": "{nds:2017:08:05,hh:11}",
"description": "Value of the stamp in JSON key:value format",
"$ref": "#/definitions/JsonNode"
}
},
"description": "Data Class to hold details"
}
And the reference JsonNode definition generated is
"definitions": {
"JsonNode": {
"type": "object",
"properties": {
"array": {
"type": "boolean"
},
"bigDecimal": {
"type": "boolean"
},
"bigInteger": {
"type": "boolean"
},
"binary": {
"type": "boolean"
},
"boolean": {
"type": "boolean"
},
"containerNode": {
"type": "boolean"
},
"double": {
"type": "boolean"
},
"float": {
"type": "boolean"
},
"floatingPointNumber": {
"type": "boolean"
},
"int": {
"type": "boolean"
},
"integralNumber": {
"type": "boolean"
},
"long": {
"type": "boolean"
},
"missingNode": {
"type": "boolean"
},
"nodeType": {
"type": "string",
"enum": [
"ARRAY",
"BINARY",
"BOOLEAN",
"MISSING",
"NULL",
"NUMBER",
"OBJECT",
"POJO",
"STRING"
]
},
"null": {
"type": "boolean"
},
"number": {
"type": "boolean"
},
"object": {
"type": "boolean"
},
"pojo": {
"type": "boolean"
},
"short": {
"type": "boolean"
},
"textual": {
"type": "boolean"
},
"valueNode": {
"type": "boolean"
}
}
}
Now, when I generate a client library with this API definition, the JsonNode Object allowed on the client side has only Boolean variables, and I cannot assign actual JSON strings to it, and hence cannot pass a JSON value to the connecting server (from which I generated the API definitions)
I was wondering is there was a way I could get to pass Json Strings from client to server using the swagger generated libraries. Or any other directions in which I can achieve the required end result.
Thanks (and apologies for the long post)
An attribute in ApiModelProperty, dataType="java.util.Map" should help
public class NewModel {
#ApiModelProperty(
example = "{ds:2017:08:05,hh:11}",
dataType = "java.util.Map"
)
private final JsonNode value;
Perhaps AlternateTypeRuleConvention, which is introduced in Springfox, will help you.
For example, you can do this:
#Bean
public AlternateTypeRuleConvention typeConvention(final TypeResolver resolver) {
return new AlternateTypeRuleConvention() {
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
#Override
public List<AlternateTypeRule> rules() {
return Collections.singletonList(
AlternateTypeRules.newRule(resolver.resolve(JsonNode.class), resolver.resolve(jsonNodeApi()))
);
}
};
}
private Type jsonNodeApi() {
return new AlternateTypeBuilder()
.fullyQualifiedClassName(
String.format("%s.generated.%s",
JsonNode.class.getPackage().getName(),
JsonNode.class.getSimpleName()))
.build();
}
AlternateTypeBuilder also allows you to specify custom fields if necessary.
I have a field in our data with multiple typing:
It could be type=string, which has the schema:
{"mixed_field" : {"type":"string"} }
Other times it could be type=object, the schema looks like:
{"mixed_field" : {
"properties": {
"access_token": {
"type": "string"
},
"created_at": {
"type": "integer"
}
},
"type": "object"
}
}
How do I express that "mixed_field" can be either type string or type object? Should I use the "oneOf" keyword as follows?
{
"mixed_field": {
"oneOf": [
{
"type": "string"
},
{
"properties": {
"access_token": {
"type": "string"
},
"created_at": {
"type": "integer"
}
},
"type": "object"
}
]
}
}
You can use oneOf/anyOf or you can use "type": ["string", "object"], in case it is a string "properties" keyword will be ignored.