我并不真正赞成使用正则表达式以既定的传输格式编组/解组数据 - 这是 Jackson 擅长的。我会把正则表达式留给非常具体的捕获场景,因为它很难维护。我设法在几分钟内完成了一个解决方案,并且应该可以直接使用 Java 11+ 开箱即用(前提是您对 apache utils、jackson、hamcrest 和 logback 有习惯依赖):
编辑:我的回复可能已经太晚了,但我支持我的决定,即不实施解析算法或使用正则表达式,而是委托给杰克逊。我已经更改了我的示例,以通过自定义反序列化提供更有弹性的处理,您可以在其中决定遇到格式错误的数据时该怎么做。也许它对其他人有帮助。
public class MyJsonDeserializer {
static final Logger logger = LoggerFactory.getLogger(MyJsonDeserializer.class);
private static String payload = "{ \n" +
" \"vinForRepair\" : \"ABCDE123455432199\",\n" +
" \"dateOfRepair\" : \"17/08/2021\",\n" +
" \"mileage\" : 100000,\n" +
" \"items\" : [ {\n" +
" \"description\" : \"Water pump\",\n" +
" \"quantity\" : 1,\n" +
" \"price\" : 120.0,\n" +
" \"metric\" : \"UNIT\"\n" +
"}, {" +
" \"description\" : \"Motor oil\",\n" +
" \"quantity\" : 1,\n" +
" \"price\" : 30.0,\n" +
" \"metric\" : \"LITER\"\n" +
" } ]\n" +
"}, {" +
" \"vinForRepair\" : \"ABCDE123455432100\",\n" +
" \"dateOfRepair\" : \"32/x8/2021\",\n" +
" \"mileage\" : 250000,\n" +
" \"items\" : [ {\n" +
" \"description\" : \"Break fluid\",\n" +
" \"quantity\" : 1,\n" +
" \"price\" : 20.0,\n" +
" \"metric\" : \"LITER\"\n" +
" }, {" +
" \"description\" : \"Tyre\",\n" +
" \"quantity\" : 2,\n" +
" \"price\" : 80.0,\n" +
" \"metric\" : \"KABOOSH\"\n" +
" } ]\n" +
"}";
static class OrderDto {
public final String vinForRepair;
public final LocalDate dateOfRepair;
public final Long mileage;
public final List<OrderItemDto> items;
@JsonCreator
public OrderDto(@JsonProperty("vinForRepair") String vinForRepair,
@JsonProperty("dateOfRepair") LocalDate dateOfRepair,
@JsonProperty("mileage") Long mileage,
@JsonProperty("items") List<OrderItemDto> items) {
this.vinForRepair = vinForRepair;
this.dateOfRepair = dateOfRepair;
this.mileage = mileage;
this.items = items;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
enum MetricEnum {
UNIT, LITER
}
static class OrderItemDto {
public final String description;
public final Integer quantity;
public final Double price;
public final MetricEnum metric;
@JsonCreator
public OrderItemDto(@JsonProperty("description") String description,
@JsonProperty("quantity") Integer quantity,
@JsonProperty("price") Double price,
@JsonProperty("metric") MetricEnum metric) {
this.description = description;
this.quantity = quantity;
this.price = price;
this.metric = metric;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
static class OrderDtoDeserializer extends StdDeserializer<OrderDto> {
public OrderDtoDeserializer(Class<?> vc) {
super(vc);
}
public OrderDtoDeserializer() {
this(null);
}
@Override
public OrderDto deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode jsonNode = jp.getCodec().readTree(jp);
String vinForRepair = jsonNode.get("vinForRepair").textValue();
LocalDate dateOfRepair = getDateOfRepair(jsonNode, ctxt);
Long mileage = jsonNode.get("mileage").longValue();
List<OrderItemDto> parsedItems = new ArrayList<>();
JsonNode items = jsonNode.get("items");
if (items.isArray()) {
ArrayNode itemsArry = (ArrayNode)items;
for (JsonNode item : itemsArry) {
String description = item.get("description").textValue();
Integer quantity = item.get("quantity").intValue();
Double price = item.get("price").doubleValue();
MetricEnum metric = getMetric(item, ctxt);
parsedItems.add(new OrderItemDto(description, quantity, price, metric));
}
}
return new OrderDto(vinForRepair, dateOfRepair, mileage, List.copyOf(parsedItems));
}
//handle any weird values
public LocalDate getDateOfRepair(JsonNode jsonNode, DeserializationContext ctxt) {
String valueToConvert = jsonNode.get("dateOfRepair").textValue();
try {
return LocalDate.parse(valueToConvert, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
} catch (DateTimeParseException ex) {
JsonLocation tokenLocation = ctxt.getParser().getTokenLocation();
logger.warn("Bad input data on: [{},{}]:", tokenLocation.getLineNr(), tokenLocation.getColumnNr());
logger.warn("{}. Will use default '{}' as date value instead.", ex.getMessage(), null);
return null;
}
}
//handle any weird values
public MetricEnum getMetric(JsonNode item, DeserializationContext ctxt) {
String valueToConvert = item.get("metric").textValue();
try {
return MetricEnum.valueOf(valueToConvert);
} catch (IllegalArgumentException ex) {
JsonLocation tokenLocation = ctxt.getParser().getTokenLocation();
logger.warn("Bad input data on: [{},{}]:", tokenLocation.getLineNr(), tokenLocation.getColumnNr());
logger.warn("Unknown '{}' value - {}. Will use default '{}' instead.", valueToConvert, ex.getMessage(), MetricEnum.UNIT);
return MetricEnum.UNIT;
}
}
}
@Test
public void deserialize() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule orderDtoModule = new SimpleModule();
orderDtoModule.addDeserializer(OrderDto.class, new OrderDtoDeserializer());
mapper.registerModules(new Jdk8Module(), orderDtoModule);
CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(List.class, OrderDto.class);
//read from string
List<OrderDto> orders = mapper.readValue("[" + payload + "]", collectionType);
Assert.assertThat(orders, Matchers.notNullValue());
Assert.assertThat(orders.size(), Matchers.is(2));
logger.info("{}", orders);
}
}