For example, I have a JSON schema looks as following:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"billing_address": { "$ref": "#/definitions/address" },
"shipping_address": { "$ref": "#/definitions/address" }
}
"definitions": {
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
}
}
This schema indicate an object with two vairable billing_address and shipping_address, both of them are of type address, which contains three properties: street_address, city and state.
Now I got another "larger" schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"billing_address": { "$ref": "#/definitions/address" },
"shipping_address": { "$ref": "#/definitions/address" },
"new_address": { "$ref": "#/definitions/address" }
}
"definitions": {
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip_code": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
}
}
As you can see, I added a new property new_address into the schema, and in address there is a new property called zip_code, which is not a required property.
So if I created an object from the old JSON schema, it should also be available for the new JSON schema. In this case, we will call the new schema is compatible with the old one. (In another word, the new schema is extension of the old one, but no modification.)
The question is how can I judge if a schema is compatible with another in Java? Complicated case should also be taken care, for example "minimum" property for a number field.
Just test it. In my current project, I am writing following contract tests:
1) having Java domain object, I serialize it to JSON and compare it to reference JSON data. I use https://github.com/skyscreamer/JSONassert for comparing two JSON strings.
For reference JSON data, you need to use 'smaller schema' object.
2) having sample JSON data, I deserialize it to my domain object, and verify if deserialization was succesfull. I compare deserialization result with model object. For sample JSON data, you shoud use your 'larger schema' object.
This test verifies if 'larger schema' JSON data is backward compatible with your 'smaller schema' domain.
I write those test at each level of my domain model -one for top-level object, and another one for each non-trivial nested object. That requires more test code and more JSON sample data, but gives much better confidence. If something fails, error messages will be fine-tuned, you will know exactly what level of hierarchy is broken (JSONAssert error messages may have many errors and be non trivial to read for deeply nested object hierarchies). So it's a trade-off between
* time spend to maintain test code and data
* quality of error messages
Such tests are fast- they need just JSON serialization/deserialization.
https://github.com/spring-cloud/spring-cloud-contract will help you writing contract test for REST APIs, messaging, etc- but for simple cases procedure I given above may be good enough
Related
Is this possible to make class in avro schema that have one of his parameter as themself?
Example in java:
public class Example {
private Integer value;
private Example example;
}
Avro schema is not defined in java, but in a json file usually with .avsc file extension. Here's an example of a recursive avro schema that represents a tree:
{
"type": "record",
"name": "Node",
"fields": [
{
"name": "value",
"type": "long"
},
{
"name": "children",
"type": { "type": "array", "items": "Node" }
}
]
}
So yes, it is perfectly possible to create recursive schemas.
See also this issue, where even a shorter schema is defined:
{
"type": "record",
"name": "RecursiveRecord",
"fields": [{"name": "child", "type": "RecursiveRecord"}]
}
I am trying to add HATEOAS links to my Controller results.
Link creation:
Link link = JaxRsLinkBuilder.linkTo(MainRestService.class)
.slash(this::getVersion)
.slash(LinkMappingConstants.TAXPAYERS)
.slash(taxPayerId)
.slash(LinkMappingConstants.ESTIMATIONS)
.slash(estimationId)
.withSelfRel();
The link itself is created fine, but there are superfluous entries in the resulting JSON:
"links": [
{
"rel": "self",
"href": "http://localhost:8080/blablabla",
"hreflang": null,
"media": null,
"title": null,
"type": null,
"deprecation": null,
"template": {
"variables": [
],
"variableNames": [
]
}
}
]
How can i get this format? (without use property spring.jackson.default-property-inclusion=NON_NULL)
"links": [
{
"rel": "self",
"href": "http://localhost:8080/blablabla"
}
]
Thanks.
As you mention, if you want NON_NULL property inclusion on the whole JSON and not just links you can use spring.jackson.default-property-inclusion=NON_NULL. This won't fix the empty template fields however.
If you want to have NON_NULL property inclusion on just the Link object using Jackson for serialization you can achieve this by using a Jackson MixIn for the Link object with the #JsonInclude(Include.NON_NULL) annotation.
As an example:
#JsonInclude(Include.NON_NULL)
abstract class LinkMixIn {
}
mapper.addMixIn(Link.class, LinkMixIn.class);
To hide the template fields you can either add #JsonIgnore if you never want the template section to be serialized, or try NON_DEFAULT property inclusion on the above answer which creates a new instance of the object and compares it to what is to be serialized to determine if it should be included.
E.g. something like the following would not serialize the result of getTemplate at all
#JsonInclude(Include.NON_NULL)
abstract class LinkMixIn {
#JsonIgnore abstract Template getTemplate();
}
I am learning avro schemas and i tried to make a little project. It seems i am stuck. I tried looking on documentation also but it seems confusing a lot.
Let's assume I have to make a schema for this class
class example implements Serializable {
private Object data;
}
What would be the corresponding avro schema(.avsc) for it?
I used reflect to get schema and got the corresponding avsc for it but when you do mvn compile, it just throws errors
{
"type": "record",
"name": "example",
"fields": [{
"name": "x1",
"type": {
"type": "map",
"values": {
"type": "record",
"name": "Object",
"namespace": "java.lang",
"fields": []
}
}
}, {
"name": "data",
"type": "java.lang.Object"
}
]
}
my question is about the fact that i want to use the same class to deserialize and re-serialize two different Jsons. I try to explain better.
I've these Jsons:
//JSON A
{
"flavors": [
{
"id": "52415800-8b69-11e0-9b19-734f1195ff37",
"name": "256 MB Server",
"ram": 256,
"OS-FLV-DISABLED:disabled":true
"links": [
{
"rel": "self",
"href": "http://www.myexample.com"
},
{
"rel": "bookmark",
"href":"http://www.myexample.com"
}
]
},
...
}
//JSON B
{
"flavors": [
{
"id": "52415800-8b69-11e0-9b19-734f1195ff37",
"name": "256 MB Server",
"links": [
{
"rel": "self",
"href": "http://www.myexample.com"
},
{
"rel": "bookmark",
"href":"http://www.myexample.com"
}
]
},
...
}
As you can see JSON B has all the fields of JSON A except "ram" and
"OS-FLV-DISABLED:disabled". The classes i used are the following:
public class Flavor {
private String name;
private List<Link> links;
private int ram;
private boolean OS_FLV_DISABLED_disabled;
//constructor and getter/setter
}
#XmlRootElement
public class GetFlavorsResponse {
private List<Flavor> flavors;
//constructor and getter/setter
}
Moreover just above the getter method isOS_FLV_DISABLED_disabled i've put the annotation #XmlElement(name = "OS-FLV-DISABLED:disabled")
otherwise Jackson doesn't recognize this property.
Here is the scheme of the situation:
When i receive JSON A there are no problems, JSON resultant is again JSON A; but when i receive JSON B the result of the process deserialization-serialization is:
//JSON C
{
"flavors": [
{
"id": "52415800-8b69-11e0-9b19-734f1195ff37",
"name": "256 MB Server",
"ram": 0,
"OS-FLV-DISABLED:disabled":false
"links": [
{
"rel": "self",
"href": "http://www.myexample.com"
},
{
"rel": "bookmark",
"href":"http://www.myexample.com"
}
]
},
...
}
Now as first thing i thought that Jackson sets class properties that was not in Json with
their default values, that is, 0 and false respectively for "ram" and
"OS-FLV-DISABLED:disabled". So i've put the annotation
#JsonSerialize(include=JsonSerialize.Inclusion.NON_DEFAULT)
just above Flavor class. This works but the problem is that when i receive JSON A in which "ram" and "OS-FLV-DISABLED:disabled" have as values 0 and false (possible situation), the result of the process mentioned above is JSON B since these two fields are ignored.
So established that this is not the solution for my problem, i read that some people suggest to use #JsonView or #JsonFilter but i don't understand how to apply these Jackson features in this case.
I hope i was clear and thanks you in advance for your help.
One thing you can try is that make your ram and OS_FLV_DISABLED_disabled as Integer and Boolean types respectively. By this if no values come in json for these two properties then they will be null. And use this annotation #JsonInclude(Include.NON_NULL) to avoid serializing null properties.
I am able to convert simple Json into java objects using Google Gson. The issue is I am not able to convert this specific type of response to a java object. I think this is due to the fact that I am not sure how to actually model the java classes for it. The following is the Json:
{
"List": {
"Items": [
{
"comment": "comment",
"create": "random",
"ID": 21341234,
"URL": "www.asdf.com"
},
{
"comment": "casdf2",
"create": "rafsdfom2",
"ID": 1234,
"Url": "www.asdfd.com"
}
]
},
"related": {
"Book": "ISBN",
"ID": "id"
}}
I haven't worked with Gson specifically, but I have worked with JSON / Java conversion in Jersey and JAXB.
I would say that the easiest way to translate the JSON text is to map every {..} to a Class and [..] to a List.
For example:
Class Data {
HistoryListClass HistoryList;
Relation related;
}
Class HistoryListClass {
List<History> history;
}
Class History {
String comment;
String create;
int ID;
String ttURL;
}
Class Relation {
String book;
String ID;
}
See also:
Converting JSON to Java
The "HistoryList" element in the JSON code should probably be written "historyList", and just looking at the code given, I suggest you change it to contatain the list of "History" objects directly.