Tomasz Ducin
23rd April 2016, Szczecin
Tomasz Ducin
3rd November 2015, Warsaw
Tomasz Ducin
18th October 2015, Ossa/Rawa Mazowiecka
Tomasz Ducin
31th August 2015, Warsaw
senior software consultant @ Cybercom Poland
trainer @ Bottega IT Solutions
blah, blah, blah...
RFC 7159 vs ECMA-404
solution architect
let's modify JSON response
but why?
oh, because so!
<?xml version="1.0" encoding="UTF-8"?>
<shiporder orderid="889923"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="shiporder.xsd">
<orderperson>John Smith</orderperson>
<shipto>
<name>Ola Nordmann</name>
<address>Langgt 23</address>
<city>4000 Stavanger</city>
<country>Norway</country>
</shipto>
<item>
<title>Empire Burlesque</title>
<note>Special Edition</note>
<quantity>1</quantity>
<price>10.90</price>
</item>
<item>
<title>Hide your heart</title>
<quantity>1</quantity>
<price>9.90</price>
</item>
</shiporder>
<xs:element name="shipto">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="shiporder">
<xs:complexType>
<xs:sequence>
<xs:element name="orderperson" type="xs:string"/>
<xs:element name="shipto">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="note" type="xs:string" minOccurs="0"/>
<xs:element name="quantity" type="xs:positiveInteger"/>
<xs:element name="price" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="orderid" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
contract agreement - no changes forcing
api communication
configuration
internal app definitions
XSD for XML
JSON Schema for JSON
http://json-schema.org/
{
"type": "boolean"
}
{
"type": "integer",
"minimum": 100,
"maximum": 200,
"multipleOf": 13,
"exclusiveMinimum": true
}
{
"type": "string"
}
true
"WJUG"
169
{
"type": "array",
"minItems": 5,
"items": {
"enum": [
"green",
"blue",
"yellow"
]
}
}
[
"green",
"blue",
"green",
"green",
"yellow",
"green"
]
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false,
"required": ["firstName", "lastName"]
}
{
"firstName": "John",
"lastName": "Lennon",
"age": 40
}
{
"firstName": "Paul",
"lastName": "McCartney"
}
{
...
"properties": {
"storage": {
"type": "object",
"oneOf": [
{ "$ref": "#/definitions/diskDevice" },
{ "$ref": "#/definitions/diskUUID" },
{ "$ref": "#/definitions/nfs" },
{ "$ref": "#/definitions/tmpfs" }
]
}
}
...
}
$ref, oneOf, allOf, anyOf
>>> from jsonschema import validate
>>> schema = {
... "type" : "object",
... "properties" : {
... "price" : {"type" : "number"},
... "name" : {"type" : "string"},
... },
... }
>>> # SUCCESS: nothing happens :)
>>> validate({"name" : "Eggs",
"price" : 34.99}, schema)
>>> # ERROR
>>> validate({"name" : "Eggs",
"price" : "Invalid"}, schema)
Traceback (most recent call last):
...
ValidationError: 'Invalid' is not
of type 'number'
https://github.com/aromanovich/jsl
>>> class User(jsl.Document):
... id = jsl.StringField(required=True)
... login = jsl.StringField(required=True,
... min_length=3, max_length=20)
>>> User.get_schema(ordered=True) # or False
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"id": {"type": "string"},
"login": {
"type": "string",
"minLength": 3,
"maxLength": 20
}
},
"required": ["id", "login"]
}
<dependency>
<groupId>com.github.fge</groupId>
<artifactId>json-schema-validator</artifactId>
<version>...</version>
</dependency>
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
...
final JsonNode fstabSchema = Utils.loadResource("my-schema.json");
final JsonNode good = Utils.loadResource("json-good.json");
final JsonNode bad = Utils.loadResource("json-bad.json");
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
final JsonSchema schema = factory.getJsonSchema(fstabSchema);
ProcessingReport report;
report = schema.validate(good);
System.out.println(report);
report = schema.validate(bad);
System.out.println(report);
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>...</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>...</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>...</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jsonSchema</artifactId>
<version>...</version>
<dependency>
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
...
class POJO... {
@JsonIgnore
private String name;
@JsonProperty
public String getName() {
return name;
}
@JsonIgnore
public void setName(String name) {
this.name = name;
}
...
}
...
@JsonIgnore
private CustomType type;
@JsonProperty
public CustomType getType() {
return type;
}
@JsonIgnore
public void setType(CustomType type) {
this.type = type;
}
{
"type" : "array",
"items" : {
"type" : "object",
"id" : "urn:jsonschema:mypackage:model:POJOClass",
"properties" : {
"name" : {
"type" : "string"
},
"type" : {
"type" : "string",
"enum" : [
"COMPANY_INFO",
"CUSTOMER_INFO",
"TRADER_INFO",
...
]
}
}
}
}
public enum CustomType {
COMPANY_INFO("tcmpi"),
CUSTOMER_INFO("tcsti"),
TRADER_INFO("ttrdi"),
...
...
}
/songs
get
post
/{songId}
get
/file-content
get
post
/artists
get
post
/{artistId}
get
/albums
get
/albums
get
post
/{albumId}
get
/songs
get
/songs:
description: Collection of available songs in Jukebox
get:
description: Get a list of songs based on the song title.
queryParameters:
songTitle:
description: "The title of the song to search (...)"
required: true
minLength: 3
type: string
example: "Get L"
[...]
[...]
responses:
200:
body:
application/json:
example: |
"songs": [
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky"
},
{
"songId": "550e8400-e29b-41d4-a716-446655440111",
"songTitle": "Loose yourself to dance"
},
{
"songId": "550e8400-e29b-41d4-a716-446655440222",
"songTitle": "Gio sorgio by Moroder"
}
]
API Blueprint
Swagger
RAML:
RESTful API Modelling Language
[...]
responses:
200:
body:
application/json:
schema: |
{
"type": "object",
"$schema": "http://json-schema.org/draft-03/schema",
"id": "http://jsonschema.net",
"required": true,
"properties": {
"songTitle": {
"type": "string",
"required": true
},
"albumId": {
"type": "string",
"required": true,
"minLength": 36,
"maxLength": 36
}
}
}
vs
imbues providers with insight into their consumer obligations, and focuses service evolution around the delivery of the key business functionality demanded by consumers.
- Martin Fowler (here)