# FASTJSON v2 JSONSchema的支持

在fastjson 2.0.4版本之后，提供了JSONSchema的支持，具体JSON Schema的规范，参考 https://json-schema.org/

## 1. FASTJSON2 JSONSchema的性能
一贯如此，FASTJSON2 JSONSchema性能非常出色，远超竞品。如下的测试表名，fastjson2性能是networknt的9倍，everit的6倍。

```java
Benchmark                       Mode  Cnt   Score   Error   Units
JSONSchemaBenchmark.everit     thrpt    5   3.182 ± 0.018  ops/ms
JSONSchemaBenchmark.fastjson2  thrpt    5  21.408 ± 0.147  ops/ms
JSONSchemaBenchmark.networknt  thrpt    5   2.337 ± 0.007  ops/ms
```

* 测试代码 https://github.com/alibaba/fastjson2/blob/main/benchmark/src/main/java/com/alibaba/fastjson2/benchmark/jsonschema/JSONSchemaBenchmark.java

## 2. 通过构造JSONSchema对象直接校验

```java
@Test
public void test() {
    // 定义必须包含longitude和latitude两个属性，其中longitude的取值范围是[-180 ~ 180]，latitude的取值范围是[-90, 90]
    JSONSchema schema = JSONSchema.of(JSON.parseObject("{" +
        "  \"type\": \"object\"," +
        "  \"properties\": {" +
        "    \"longitude\": { \"type\": \"number\", \"minimum\":-180, \"maximum\":180}," +
        "    \"latitude\": { \"type\": \"number\", \"minimum\":-90, \"maximum\":90}," +
        "  }," +
        "  \"required\": [\"longitude\", \"latitude\"]" +
        "}"));

    // 校验JSONObject对象，校验通过
    assertTrue(
            schema.isValid(
                    JSONObject.of("longitude", 120.1552, "latitude", 30.2741)
            )
    );

    // 校验JSONObject失败，longitude超过最大值
    assertFalse(
            schema.isValid(
                    JSONObject.of("longitude", 220.1552, "latitude", 30.2741)
            )
    );

    // 校验JavaBean对象，校验通过
    assertTrue(
            schema.isValid(
                    new Point(120.1552, 30.2741)
            )
    );

    // 校验JavaBean对象，校验失败，latitude超过最大值
    assertFalse(
            schema.isValid(
                    new Point(120.1552, 130.2741)
            )
    );
}

public static class Point {
    public final double longitude;
    public final double latitude;

    public Point(double longitude, double latitude) {
        this.longitude = longitude;
        this.latitude = latitude;
    }
}
```

## 3. 在JSONField上配置schema
2.0.4版本后，可以在Annotation JSONField上配置schema校验输入的json数据

```java
public static class Point1 {
    @JSONField(schema = "{'minimum':-180,'maximum':180}")
    public double longitude;


    @JSONField(schema = "{'minimum':-90,'maximum':90}")
    public double latitude;
}

@Test
public void test1() {
    // parseObject 校验通过
    JSON.parseObject("{\"longitude\":120.1552,\"latitude\":30.2741}", Point1.class);

    // JSONObject to JavaObject，校验通过
    JSONObject.of("longitude", 120.1552, "latitude", 30.2741)
            .to(Point1.class);

    // parseObject 校验失败，longitude超过最大值
    assertThrows(JSONSchemaValidException.class, () ->
        JSON.parseObject("{\"longitude\":220.1552,\"latitude\":30.2741}", Point1.class)
    );

    // 校验JSONObject失败，longitude超过最大值
    assertThrows(JSONSchemaValidException.class, () ->
            JSONObject.of("longitude", 220.1552, "latitude", 30.2741)
                    .to(Point1.class)
    );
}
```

## 4. 在JSONType上配置schema
2.0.4版本后，可以在Annotation JSONType上配置schema校验输入的json数据

```java
@JSONType(schema = "{'properties':{'longitude':{'type':'number','minimum':-180,'maximum':180},'latitude':{'type':'number','minimum':-90,'maximum':90}}}")
public static class Point2 {
    @JSONField(schema = "{'minimum':-180,'maximum':180}")
    public double longitude;


    @JSONField(schema = "{'minimum':-90,'maximum':90}")
    public double latitude;
}

@Test
public void test2() {
    // parseObject 校验通过
    JSON.parseObject("{\"longitude\":120.1552,\"latitude\":30.2741}", Point2.class);

    // JSONObject to JavaObject，校验通过
    JSONObject.of("longitude", 120.1552, "latitude", 30.2741)
            .to(Point2.class);

    // parseObject 校验失败，longitude超过最大值
    assertThrows(JSONSchemaValidException.class, () ->
            JSON.parseObject("{\"longitude\":220.1552,\"latitude\":30.2741}", Point2.class)
    );

    // 校验JSONObject失败，longitude超过最大值
    assertThrows(JSONSchemaValidException.class, () ->
            JSONObject.of("longitude", 220.1552, "latitude", 30.2741)
                    .to(Point2.class)
    );
}
```

# 5. 通过类型构造JSONSchema
在后端和前端交互时，需要将java类型转换成JSONSchema返回给客户端。
```java
@Test
public void test() {
    JSONSchema schema = JSONSchema.of(Bean.class);
    String string = schema.toString();
    assertEquals(
            "{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"}},\"required\":[\"id\"]}",
            string
    );
    JSONSchema pased = JSONSchema.of(JSON.parseObject(string));
    assertTrue(Differ.diff(schema, pased));

    Bean bean = new Bean();
    JSONSchema valueSchema = JSONSchema.ofValue(bean);
    assertTrue(Differ.diff(schema, valueSchema));
}

public static class Bean {
    public int id;
    public String name;
}
```

# 6. 通过值对象构造JSONSchema
```java
@Test
public void fromValueMap() {
    Map map = new HashMap();
    map.put("id", 123);
    assertEquals("{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"}}}", JSONSchema.ofValue(map).toString());
}
```
