【发布时间】:2021-07-20 20:36:56
【问题描述】:
我创建了一个最小的应用程序来调试以下问题:
应用程序正在创建一个包含 GeoJsonPoint 的 Java 对象。当对象根据自动创建的 json 模式进行验证时,我在多次启动应用程序时会收到不同的结果。例如,我启动应用程序 5 次,结果是“Json 无效!”。当我再次启动应用程序时,我收到“Json 成功验证”的结果。
json 无效时的错误消息告诉我: /geoPosition/coordinates: 实例类型(数组)不匹配任何允许的原始类型(允许:[object])
以下行随机返回不同的 json 架构:
JsonNode fstabSchema = schemaFactory.createSchema(inputObj.getClass());
我不明白这是随机发生的。以前有人见过这种行为吗?
为了在运行时排除依赖问题,我创建了一个包含所有依赖项的 jar(jar-with-dependencies)
以下是我的文件:
pom.xml 依赖:
<dependencies>
<dependency>
<groupId>com.github.reinert</groupId>
<artifactId>jjschema</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>com.github.java-json-tools</groupId>
<artifactId>json-schema-validator</artifactId>
<version>2.2.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.2.2</version>
</dependency>
</dependencies>
App.java:
package de.s2.json.test;
import java.util.ArrayList;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
public class App
{
public static void main( String[] args )
{
Address address = new Address();
address.setCountry("Deutschland");
GeoJsonPoint geoPoint = new GeoJsonPoint(12, 23);
address.setGeoPosition(geoPoint);
ArrayList<String> ret = null;
try {
ret = Toolbox.validateJson(address);
} catch (ProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(ret != null) {
System.out.println("Json not valid!");
for (int i = 0; i < ret.size(); i++) {
System.out.println(ret.get(i));
}
} else {
System.out.println("Json successfully validated");
}
}
}
工具箱.java:
package de.s2.json.test;
import java.util.ArrayList;
import java.util.Iterator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.reinert.jjschema.v1.JsonSchemaV4Factory;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingMessage;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
public class Toolbox {
public static <T> ArrayList<String> validateJson(T inputObj) throws ProcessingException {
com.github.reinert.jjschema.v1.JsonSchemaFactory schemaFactory = new JsonSchemaV4Factory();
schemaFactory.setAutoPutDollarSchema(true);
JsonNode fstabSchema = schemaFactory.createSchema(inputObj.getClass()); // <= here I get different results
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
final JsonSchema schema = factory.getJsonSchema(fstabSchema);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode baseReceiptJson = objectMapper.convertValue(inputObj, JsonNode.class);
ProcessingReport report;
report = schema.validate(baseReceiptJson);
ArrayList<String> validationErrorDetails = new ArrayList<String>();
if (!report.isSuccess()) {
StringBuilder builder = new StringBuilder();
builder.append("Not all required fields are filled with data");
builder.append(System.getProperty("line.separator"));
for (Iterator<ProcessingMessage> i = report.iterator(); i.hasNext();) {
ProcessingMessage msg = i.next();
builder.append(msg.asJson().findValue("instance").findValue("pointer").toString());
builder.append(": ");
builder.append(msg.getMessage());
String detail = msg.asJson().findValue("instance").findValue("pointer").toString() + ": " + msg.getMessage();
detail = detail.replace("\"", "");
validationErrorDetails.add(detail);
builder.append("\n");
}
return validationErrorDetails;
}
return null;
}
}
地址.java:
package de.s2.json.test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.github.reinert.jjschema.Attributes;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Address
{
@JsonProperty("country")
private String country = "";
@JsonProperty("geoPosition")
@Attributes(required=false, description="longitude and latitude (it is initialized with 0,0)")
private GeoJsonPoint geoPosition = new GeoJsonPoint(0, 0);
}
非常感谢您的支持!
更新 1: 回答 Hiran Chaudhuri
如果验证失败,则架构如下所示:
{
"type": "object",
"properties": {
"country": {
"type": "string"
},
"geoPosition": {
"type": "object",
"properties": {
"coordinates": {
"type": "object"
},
"TYPE": {
"type": "string"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
}
},
"description": "longitude and latitude (it is initialized with 0,0 which is inside the ocean)"
}
},
"$schema": "http://json-schema.org/draft-04/schema#"
}
如果成功,则如下所示:
{
"type": "object",
"properties": {
"country": {
"type": "string"
},
"geoPosition": {
"type": "object",
"properties": {
"coordinates": {
"type": "array",
"items": {
"type": "number"
}
},
"TYPE": {
"type": "string"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
}
},
"description": "longitude and latitude (it is initialized with 0,0 which is inside the ocean)"
}
},
"$schema": "http://json-schema.org/draft-04/schema#"
}
正如已经在错误消息中指出的那样,一次坐标是“对象”,另一次是“数组”
更新 2
由于这个问题阻止了我继续我的项目,我添加了一个“丑陋的黑客”来检查架构是否包含 GeoJsonPoint。如果包含,它将使用正确的值对其进行修补。
package de.s2.json.test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.reinert.jjschema.v1.JsonSchemaV4Factory;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingMessage;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
public class Toolbox {
public static <T> ArrayList<String> validateJson(T inputObj) throws ProcessingException {
com.github.reinert.jjschema.v1.JsonSchemaFactory schemaFactory = new JsonSchemaV4Factory();
schemaFactory.setAutoPutDollarSchema(true);
JsonNode fstabSchema = schemaFactory.createSchema(inputObj.getClass());
ArrayList<String> validationErrorDetails = new ArrayList<String>();
// *****************************************************************************************
// /!\ this is an ugly hack /!\
// Randomly the function createSchema() returns an 'array' or 'object' type for coordinates
// of the geoJsonPoint. The correct value should be array. The following code checks if a
// geoJsonPoint is inside the schema and overwrites it with the correct value.
// TODO: fix this issue correctly
// *****************************************************************************************
try {
String jsonCoordinate = "{\"type\": \"array\",\"items\": {\"type\": \"number\"}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNodeCoordinate = mapper.readTree(jsonCoordinate);
JsonNode coordJsonNode = fstabSchema.findValue("geoPosition").get("properties");
ObjectNode coordObjNode = (ObjectNode) coordJsonNode;
coordObjNode.set("coordinates", jsonNodeCoordinate);
} catch (JsonProcessingException e) {
validationErrorDetails.add("Could not patch geoPosition");
return validationErrorDetails;
} catch (IOException e) {
validationErrorDetails.add("Could not patch geoPosition");
return validationErrorDetails;
} catch (NullPointerException e) {
// this means that geoPosition could not be found
// we do nothing ...
}
// ******************
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
final JsonSchema schema = factory.getJsonSchema(fstabSchema);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode baseReceiptJson = objectMapper.convertValue(inputObj, JsonNode.class);
ProcessingReport report;
report = schema.validate(baseReceiptJson);
if (!report.isSuccess()) {
StringBuilder builder = new StringBuilder();
builder.append("Not all required fields are filled with data");
builder.append(System.getProperty("line.separator"));
for (Iterator<ProcessingMessage> i = report.iterator(); i.hasNext();) {
ProcessingMessage msg = i.next();
builder.append(msg.asJson().findValue("instance").findValue("pointer").toString());
builder.append(": ");
builder.append(msg.getMessage());
String detail = msg.asJson().findValue("instance").findValue("pointer").toString() + ": " + msg.getMessage();
detail = detail.replace("\"", "");
validationErrorDetails.add(detail);
builder.append("\n");
}
return validationErrorDetails;
}
return null;
}
}
【问题讨论】: