JavaRush /Java Blog /Random EN /Json schema: why and who needs it

Json schema: why and who needs it

Published in the Random EN group
Hello stranger. Today I want to tell you about a little magic. You've probably already heard about json. This is such a universal language: it is understood by a machine and easily read by a person. Here is a typical json message example:
{
   "помещение":{
      "название":"избушка",
      "разумна":true
   },
   "основание":{
      "тип":"курьи ноги",
      "количество":2
   },
   "проживающие":[
      {
         "Name":"Баба Яга",
         "профиль":"ведьма"
      }
   ],
   "местоположение":{
      "address":"граница леса"
   }
}
It's good to talk like that, right? If before that you did not know what json is, now you know. How to use it in java code? Json has become a universal format. It stands for JavaScript Object Notation, but has long gone beyond javascript and is used almost everywhere. There are several libraries in java that make it easier to work with json. Here are the most famous: I will use the second one. There are 2 versions of them codehaus and fasterxml , I didn’t notice any differences in them, so you can use any one here. Here is a piece of code:
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue("сюда json", "сюда класс");
will help translate json into an object. And we are approaching the most important thing. Writing a class for this json. You can do it manually, create a structure like this:
-----------------------------------com.fairytale.Base.java-----------------------------------

package com.fairytale;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;


@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"type",
"quantity"
})
public class Base {

@JsonProperty("type")
public String type = "";
@JsonProperty("quantity")
public int quantity = 0;

}
-----------------------------------com.fairytale.Hut.java-----------------------------------

package com.fairytale;

import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;


@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"room",
"base",
"residents",
"location"
})
public class Hut {

@JsonProperty("room")
public Room room;
@JsonProperty("base")
public Base base;
@JsonProperty("residents")
public List<Resident> residents = new ArrayList<Resident>();
@JsonProperty("location")
public Location location;

}
-----------------------------------com.fairytale.Location.java-----------------------------------

package com.fairytale;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;


@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"address"
})
public class Location {

@JsonProperty("address")
public String address = "";

}
-----------------------------------com.fairytale.Resident.java-----------------------------------

package com.fairytale;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;


@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"name",
"profile"
})
public class Resident {

@JsonProperty("name")
public String name = "";
@JsonProperty("profile")
public String profile = "";

}
-----------------------------------com.fairytale.Room.java-----------------------------------

package com.fairytale;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"name",
"reasonable"
})
public class Room {

@JsonProperty("name")
public String name = "";
@JsonProperty("reasonable")
public boolean reasonable = false;

}
I deliberately omitted getters, setters, constructors and other pojo attributes, otherwise you would get tired of squandering =) Now look here:
{
  "definitions": {},
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/root.json",
  "type": "object",
  "title": "The Root Schema",
  "required": [
    "room",
    "base",
    "residents",
    "location"
  ],
  "properties": {
    "room": {
      "$id": "#/properties/room",
      "type": "object",
      "title": "The Room Schema",
      "required": [
        "name",
        "reasonable"
      ],
      "properties": {
        "name": {
          "$id": "#/properties/room/properties/name",
          "type": "string",
          "title": "The Name Schema",
          "default": "",
          "examples": [
            "избушка"
          ],
          "pattern": "^(.*)$"
        },
        "reasonable": {
          "$id": "#/properties/room/properties/reasonable",
          "type": "boolean",
          "title": "The Reasonable Schema",
          "default": false,
          "examples": [
            true
          ]
        }
      },
	"additionalProperties": false
    },
    "base": {
      "$id": "#/properties/base",
      "type": "object",
      "title": "The Base Schema",
      "required": [
        "type",
        "quantity"
      ],
      "properties": {
        "type": {
          "$id": "#/properties/base/properties/type",
          "type": "string",
          "title": "The Type Schema",
          "default": "",
          "examples": [
            "курьи ноги"
          ],
          "pattern": "^(.*)$"
        },
        "quantity": {
          "$id": "#/properties/base/properties/quantity",
          "type": "integer",
          "title": "The Quantity Schema",
          "default": 0,
          "examples": [
            2
          ]
        }
      },
	"additionalProperties": false
    },
    "residents": {
      "$id": "#/properties/residents",
      "type": "array",
      "title": "The Residents Schema",
      "items": {
        "$id": "#/properties/residents/items",
        "type": "object",
        "title": "The Items Schema",
        "required": [
          "name",
          "profile"
        ],
        "properties": {
          "name": {
            "$id": "#/properties/residents/items/properties/name",
            "type": "string",
            "title": "The Name Schema",
            "default": "",
            "examples": [
              "Баба Яга"
            ],
            "pattern": "^(.*)$"
          },
          "profile": {
            "$id": "#/properties/residents/items/properties/profile",
            "type": "string",
            "title": "The Profile Schema",
            "default": "",
            "examples": [
              "ведьма"
            ],
            "pattern": "^(.*)$"
          }
        },
	    "additionalProperties": false
      }
    },
    "location": {
      "$id": "#/properties/location",
      "type": "object",
      "title": "The Location Schema",
      "required": [
        "address"
      ],
      "properties": {
        "address": {
          "$id": "#/properties/location/properties/address",
          "type": "string",
          "title": "The Address Schema",
          "default": "",
          "examples": [
            "граница леса"
          ],
          "pattern": "^(.*)$",
		  "additionalProperties": false
        }
      },
	"additionalProperties": false
    }
  },
	"additionalProperties": false
}
This is the json schema of the structure above. Now it's time to explain why you need it. It eliminates the need to write classes and maintain them. There is such a good project jsonschema2pojo . It offers plugins for project builders (maven, grade) that will write these classes for you at build time. Here's an example from my project:
<plugin>
    <groupId>org.jsonschema2pojo</groupId>
    <artifactId>jsonschema2pojo-maven-plugin</artifactId>
    <version>0.4.37</version>

    <executions>
        <execution>
            <id>jsonschema2opjo</id>
            <configuration>
                <sourceDirectory>${project.basedir}/src/main/resources/json-schema/</sourceDirectory>
                <targetPackage>tester.model</targetPackage>
                <outputDirectory>${project.basedir}/target/generated-sources/jsonschema/</outputDirectory>
                <useCommonsLang3>true</useCommonsLang3>
                <includeConstructors>true</includeConstructors>
                <generateBuilders>true</generateBuilders>
                <includeToString>true</includeToString>
                <usePrimitives>true</usePrimitives>
            </configuration>
            <goals>
                <goal>generate</goal>
            </goals>
            <phase>generate-sources</phase>
        </execution>
    </executions>
</plugin>
This is his setting. The most interesting here:
<useCommonsLang3>true</useCommonsLang3>
<includeConstructors>true</includeConstructors>
<generateBuilders>true</generateBuilders>
<includeToString>true</includeToString>
<usePrimitives>true</usePrimitives>
This is an indication how to write a class: useCommonsLang3 - use the CommonsLang3 library includeConstructors - write a constructor generateBuilders - inline the builder pattern includeToString - add toString usePrimitives - an instruction to use primitives Why is this better than self-written code?
  1. You can customize classes with one line. For example, you need to add the Pojo suffix to each class. It is enough to add <classNameSuffix>Pojo</classNameSuffix> to build the project - and you're done. Otherwise, we would have to change the names of each class by hand.

    There are a lot of these parameters, you should read about all of them in the docks

  2. If your project has a consumer, it will be much easier to give it json schemas, rather than java classes. As I said, schemes are universal and the consumer will simply generate a pojo in their own language.

  3. They are much smaller. The example above contains a lot of information that is not always needed, for example, patterns and examples. But if you return them to java code, it will also grow a lot. And do not forget about the template code, which is indicated in the schemes by a couple of settings in the plugin, and in the code you need to write it yourself. And yes, I know about lombok, there is an alternative.

  4. No logic in pojo. When your classes are self-written, someone may be tempted to add a convenient method. You cannot add a method to schema json, as well as to a generated class.

Probably, that's all.

Conclusion:

I tried to convey that json schemas are a very good format for interaction between projects. Once I met him on a project, and he sunk into my heart. I use them almost everywhere. Yes, this is not always convenient, because in order to see the source code, you need to build the project. But this is pojo, which means that there can be no logic there, and there won’t be any with schemes. PS .. sometimes I explain badly, so here is a video with a good report: Kirill Merkushev - Code generation as a way to solve automator problems.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION