简体   繁体   中英

Validating JSON Schema in java

I am working on a POC for an API call and I need to validate the request for special character. So if an invalid special character exist in the request, it should throw an exception. I am using spring boot and here are all my files,

StudentController.java

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kjetland.jackson.jsonSchema.JsonSchemaConfig;
import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator;
import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
public class StudentController {

    @PostMapping("/student")
    public Student home(@Valid @RequestBody Student student) throws JsonProcessingException {
        JSONObject jsonObject = new JSONObject(student.getAddress());
        ObjectMapper mapper = new ObjectMapper();
        JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper, JsonSchemaConfig.nullableJsonSchemaDraft4());
        JsonNode addressSchema = schemaGen.generateJsonSchema(Address.class);
        String jsonSchemaAsString = mapper.writeValueAsString(addressSchema);
        JSONObject jsonSchema = new JSONObject(jsonSchemaAsString);
        System.out.println("JSON: "+jsonObject);
        System.out.println("jsonSchema: "+jsonSchema);
        Schema schema = SchemaLoader.load(jsonSchema);
        schema.validate(jsonObject);
        return student;
    }
}

Student.java

import java.util.Objects;

public class Student {
    private String name;

    private int age;

    private Object address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Object getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return age == student.age &&
                name.equals(student.name) &&
                address.equals(student.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, address);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}

Address.java

import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaInject;
import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaString;

import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Objects;

@JsonSchemaInject(strings = {@JsonSchemaString(path = "patternProperties/^[a-zA-Z0-9@]*$/type", value = "string")})
public class Address {
    @NotNull
    private String street;
    private String state;
    private int zipCode;
    @NotNull
    private List<String> phoneNo;
    @NotNull
    private List<Country> country;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public int getZipCode() {
        return zipCode;
    }

    public void setZipCode(int zipCode) {
        this.zipCode = zipCode;
    }

    public List<String> getPhoneNo() {
        return phoneNo;
    }

    public void setPhoneNo(List<String> phoneNo) {
        this.phoneNo = phoneNo;
    }

    public List<Country> getCountry() {
        return country;
    }

    public void setCountry(List<Country> country) {
        this.country = country;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Address)) return false;
        Address address = (Address) o;
        return zipCode == address.zipCode &&
                Objects.equals(street, address.street) &&
                Objects.equals(state, address.state) &&
                Objects.equals(phoneNo, address.phoneNo) &&
                Objects.equals(country, address.country);
    }

    @Override
    public int hashCode() {
        return Objects.hash(street, state, zipCode, phoneNo, country);
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", state='" + state + '\'' +
                ", zipCode=" + zipCode +
                ", phoneNo=" + phoneNo +
                ", country=" + country +
                '}';
    }
}

Country.java

import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaInject;
import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaString;

import java.util.Objects;

@JsonSchemaInject(strings = {@JsonSchemaString(path = "patternProperties/^[a-zA-Z0-9@]*$/type", value = "string")})
public class Country {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Country)) return false;
        Country country = (Country) o;
        return Objects.equals(name, country.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public String toString() {
        return "Country{" +
                "name='" + name + '\'' +
                '}';
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xyz</groupId>
    <artifactId>annotation-validator</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>annotation-validator</name>
    <description>Demo project for student response</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.github.everit-org.json-schema</groupId>
            <artifactId>org.everit.json.schema</artifactId>
            <version>1.9.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.kjetland/mbknor-jackson-jsonschema -->
        <dependency>
            <groupId>com.kjetland</groupId>
            <artifactId>mbknor-jackson-jsonschema_2.12</artifactId>
            <version>1.0.31</version>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>jitpack.io</id>
            <url>https://jitpack.io</url>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

and when I make a request using the following request body

{
    "name": "John",
    "age": 99,
    "address": {
        "street": "crab apple ln",
        "state": "florida",
        "zipCode": 99174,
        "phoneNo": [
            "Hello",
            "World"
        ],
        "country": [
            {
                "name": "China"
            },
            {
                "name": "USA"
            }
        ]
    }
}

I am getting the following error

{
    "timestamp": "2018-12-06T05:01:14.009+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "#: 3 schema violations found",
    "path": "/student"
}

Based on the above error, even when I am sending a valid request, it is returning back a schema violations found error

As for the output of those 2 println statements, here it is,

JSON: {"country":[{"name":"China"},{"name":"USA"}],"zipCode":99174,"street":"crab apple ln","state":"florida","phoneNo":["Hello","World"]}

jsonSchema: {"patternProperties":{"^[a-zA-Z0-9@]*$":{"type":"string"}},"$schema":"http://json-schema.org/draft-04/schema#","additionalProperties":false,"title":"Address","type":"object","definitions":{"Country":{"patternProperties":{"^[a-zA-Z0-9@]*$":{"type":"string"}},"additionalProperties":false,"type":"object","properties":{"name":{"oneOf":[{"type":"null","title":"Not included"},{"type":"string"}]}}}},"properties":{"zipCode":{"type":"integer"},"country":{"type":"array","items":{"$ref":"#/definitions/Country"}},"street":{"type":"string"},"state":{"oneOf":[{"type":"null","title":"Not included"},{"type":"string"}]},"phoneNo":{"type":"array","items":{"type":"string"}}},"required":["street","zipCode","phoneNo","country"]}

Remove the patternProperties . Why did you even add them?

Try using an online schema validator, eg https://www.jsonschemavalidator.net/ , with the strings from the print statements, and you'll see better error messages, which go away if you remove the patternProperties .

This is because the patternProperties is saying that all properties where the key is alphanumeric (matches ^[a-zA-Z0-9@]*$ ) must be a type string , which obviously is not true for zipCode (type integer ), country (type array ), and phoneNo (type array ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM