考虑一个用例,其中给出了一个XML Schema.xsd文件,您应该基于该文件将XML转换为JSON格式。对此没有直接的解决方案。我们需要做以下工作:-
我们希望尽可能地自动化这一点,以便在XML模式中有任何更新时,可以以最小的更改进行调整。我们将在这里创建一个JAXB任务(gradle或maven),它与项目构建阶段绑定,负责从给定的schema.xsd文件生成Java类文件。
当我们使用生成的Java类文件将XML转换为JSON文件时,我们发现主要有两个问题不符合我们的要求,并解决了它们:-
按照步骤自动生成类文件并解决以上两个问题:-
Steps
1.添加Gradle任务以生成类
在build.gradle中添加jaxb gradle任务,其中您指定了以下内容:-
build.gradle
configurations {
jaxb
}
// Dependencies to be used by "jaxb" task
dependencies {
jaxb(
'com.sun.xml.bind:jaxb-xjc:2.3.1',
'com.sun.xml.bind:jaxb-impl:2.3.1',
'org.glassfish.jaxb:jaxb-runtime:2.3.1',
'org.jvnet.jaxb2_commons:jaxb2-basics:0.12.0'
)
}
// JAXB task definition
task jaxb {
def generatedResouces = "src/main/generated-sources"
def jaxbTargetDir = file(generatedResouces)
jaxbTargetDir.deleteDir()
doLast {
jaxbTargetDir.mkdirs()
ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.jaxb.asPath)
ant.jaxbTargetDir = jaxbTargetDir
ant.xjc(destDir: '${jaxbTargetDir}', package: 'com.example.jaxb', extension: true){
schema(dir: "src/main/resources/schema", includes: "schema.xsd")
binding(dir: "src/main/resources/jaxb", includes: "bindings.xjb")
arg(line: '-XenumValue')
}
}
}
// Add generated classes directory to source
sourceSets.main.java.srcDirs += 'src/main/generated-sources'
// Run jaxb task before compile Java classes
compileJava.dependsOn jaxb
2.从多个XSD模式文件生成类
如果需要从不同包中的多个XML schema.xsd文件生成类,可以添加多个ant.xjc是这样的:-
ant.xjc(destDir: '${jaxbTargetDir}', package: 'com.example.jaxb.schema1', extension: true){
schema(dir: "src/main/resources/schema", includes: "schema1.xsd")
binding(dir: "src/main/resources/jaxb", includes: "bindings.xjb")
arg(line: '-XenumValue')
}
ant.xjc(destDir: '${jaxbTargetDir}', package: 'com.example.jaxb.schema2', extension: true){
schema(dir: "src/main/resources/schema", includes: "schema2.xsd")
binding(dir: "src/main/resources/jaxb", includes: "bindings.xjb")
arg(line: '-XenumValue')
}
它将从包com.example.jaxb中的schema1.xsd生成类。包com.example.jaxb.schema2中的schema1和schema2.xsd
修复日期和时间格式问题
AXB将xs:time、xs:date和xs:dateTime映射到javax.xml.datatype。默认情况下为XMLGregorianCalendar。
XMLGregorianCalendar缺乏底层数据类型的语义:
为了避免这种情况,我们希望JAXB映射:-
为此,我们需要做两件事,首先为例如com.example.xml.adapter创建适配器类。然后告诉JAXB通过绑定bindingsxjb文件。
TimeAdapter.java
package com.example.xml.adapter;
public class TimeAdapter extends XmlAdapter<String, LocalTime> {
@Override
public LocalTime unmarshal(String v) {
if (Objects.nonNull(v)) {
try {
return LocalTime.parse(v);
} catch (DateTimeParseException e) {
throw new RuntimeException("Failed to parse time: " + v, e);
}
}
return null;
}
@Override
public String marshal(LocalTime v) {
if (Objects.nonNull(v)) {
return v.format(DateTimeFormatter.ISO_TIME);
}
return null;
}
}
bindings.xjb
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings version="2.1"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
<jaxb:globalBindings typesafeEnumMaxMembers="2000">
<xjc:serializable uid="-1"/>
<xjc:javaType xmlType="xs:date"
name="java.time.LocalDate"
adapter="com.example.xml.adapter.DateAdapter"/>
<xjc:javaType xmlType="xs:time"
name="java.time.LocalTime"
adapter="com.example.xml.adapter.TimeAdapter"/>
<xjc:javaType xmlType="xs:dateTime"
name="java.time.LocalDateTime"
adapter="com.example.xml.adapter.DateTimeAdapter"/>
</jaxb:globalBindings>
</jaxb:bindings>
就这样!现在,生成的类将具有映射到Java时间包的时间、日期和日期时间。
3.修复枚举值问题
首先看问题陈述,下面是xsd模式中枚举的示例:-
schema.xsd
<xs:simpleType name = "roundingDirection">
<xs:restriction base = "xs:string">
<xs:enumeration value = "up"/>
<xs:enumeration value = "half up"/>
<xs:enumeration value = "down"/>
<xs:enumeration value = "half down"/>
<xs:enumeration value = "nearest"/>
</xs:restriction>
</xs:simpleType>
JAXB从模schema.xsd文件生成枚举类:-
RoundingDirection.java
public enum RoundingDirection {
@XmlEnumValue("up")
UP("up"),
@XmlEnumValue("half up")
HALF_UP("half up"),
@XmlEnumValue("down")
DOWN("down"),
@XmlEnumValue("half down")
HALF_DOWN("half down"),
@XmlEnumValue("nearest")
NEAREST("nearest");
}
使用上面生成的enum类,Jackson默认转换以下XML:-
AccountSummary.xml
<?xml version="1.0" encoding="UTF-8" ?>
<accountSummary>
<interest rounding = "half up">27.55</interest>
</accountSummary>
要遵循json:-
AccountSummary.json
{
"interest" : {
"value" : 27.55,
"rounding" : "HALF_UP"
}
}
Jackson在转换过程中默认使用枚举类的name()方法。如果我们希望在转换中使用枚举值,则需要自定义反序列化器。
我们需要做两件事来解决所有生成的枚举类:-
public class EnumValueDeserializer extends JsonSerializer<EnumValue> {
@Override
public void serialize(EnumValue value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.enumValue().toString());
}
}
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(EnumValue.class, new EnumValueDeserializer());
objectMapper.registerModule(module);
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
结论
从github/springboot.xml下载本文示例的完整源代码
原文:Convert XSD Schema to JSON using JAXB