在本文中,我演示了一种从XML Schema (XSD)生成JSON Schema的 方法 。 在概述从XML Schema创建JSON Schema的方法的同时,本文还演示了JAXB实现的用法(与JDK 9捆绑在一起的xjc版本2.2.12-b150331.1824 [build 1.9.0-ea-b68])和JSON / Java绑定实现的说明( 杰克逊 2.5.4)。
从XSD生成JSON模式的这种方法的步骤可以概括为:
- 应用JAXB的xjc编译器从XML Schema(XSD)生成Java类。
- 应用Jackson来从JAXB生成的Java类生成JSON模式。
使用JAXB的xjc从XSD生成Java类
为了便于讨论,我将使用我先前的博客文章A JAXB Nuance:字符串与枚举受限XSD字符串中的枚举使用的简单Food.xsd 。 为了方便起见,我在此处重现了该简单模式,但没有特定于先前博客文章的XML注释:
Food.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:dustin="http://marxsoftware.blogspot.com/foodxml"
targetNamespace="http://marxsoftware.blogspot.com/foodxml"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="Food">
<xs:complexType>
<xs:sequence>
<xs:element name="Vegetable" type="dustin:Vegetable" />
<xs:element ref="dustin:Fruit" />
<xs:element name="Dessert" type="dustin:Dessert" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:simpleType name="Vegetable">
<xs:restriction base="xs:string">
<xs:enumeration value="Carrot"/>
<xs:enumeration value="Squash"/>
<xs:enumeration value="Spinach"/>
<xs:enumeration value="Celery"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="Fruit">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Watermelon"/>
<xs:enumeration value="Apple"/>
<xs:enumeration value="Orange"/>
<xs:enumeration value="Grape"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:simpleType name="Dessert">
<xs:restriction base="xs:string">
<xs:enumeration value="Pie"/>
<xs:enumeration value="Cake"/>
<xs:enumeration value="Ice Cream"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
使用JDK提供的JAXB实现提供的xjc命令行工具很容易生成与此XSD对应的Java类。 下一个屏幕快照使用以下命令显示了此过程:
xjc -d jaxb。\ Food.xsd
这个简单的命令生成与提供的Food.xsd相对应的Java类,并将这些类放置在指定的“ jaxb”子目录中。
用Jackson从JAXB生成的类生成JSON
使用现在可用的JAXB生成的类,可以将Jackson应用于这些类以从Java类生成JSON。 杰克逊在其主门户页面上被描述为“一个用于处理的多功能Java库”,“受到Java平台可用的XML工具的质量和多样性的启发。” Jackson的存在以及类似的框架和库似乎是Oracle从Java SE 9中 删除 JEP 198 (“轻型JSON API”)的原因之一。 [值得注意的是, Java EE 7已经通过其JSR 353 (“与JSON处理有关的Java API”)的实现实现了内置的JSON支持 ,而该JSR 353与JEP 198无关。)
将Jackson应用于从我们的JAXB生成的Java类生成JSON的第一步就是获取并配置Jackson的ObjectMapper类的实例。 下一个代码清单显示了实现此目的的一种方法。
获取和配置用于JAXB序列化/反序列化的Jackson ObjectMapper
/**
* Create instance of ObjectMapper with JAXB introspector
* and default type factory.
*
* @return Instance of ObjectMapper with JAXB introspector
* and default type factory.
*/
private ObjectMapper createJaxbObjectMapper()
{
final ObjectMapper mapper = new ObjectMapper();
final TypeFactory typeFactory = TypeFactory.defaultInstance();
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(typeFactory);
// make deserializer use JAXB annotations (only)
mapper.getDeserializationConfig().with(introspector);
// make serializer use JAXB annotations (only)
mapper.getSerializationConfig().with(introspector);
return mapper;
}
上面的代码清单演示了如何获取Jackson的ObjectMapper实例并将其配置为使用默认类型的工厂和面向JAXB的注释自检器。
在实例化并正确配置了Jackson ObjectMapper情况下,很容易使用该ObjectMapper实例从生成的JAXB类生成JSON。 下一个代码清单中演示了使用不推荐使用的Jackson类JsonSchema实现此目的的一种方法。
使用不推荐使用的com.fasterxml.jackson.databind.jsonschema.JsonSchema类从Java类生成JSON
/**
* Write JSON Schema to standard output based upon Java source
* code in class whose fully qualified package and class name
* have been provided.
*
* @param mapper Instance of ObjectMapper from which to
* invoke JSON schema generation.
* @param fullyQualifiedClassName Name of Java class upon
* which JSON Schema will be extracted.
*/
private void writeToStandardOutputWithDeprecatedJsonSchema(
final ObjectMapper mapper, final String fullyQualifiedClassName)
{
try
{
final JsonSchema jsonSchema = mapper.generateJsonSchema(Class.forName(fullyQualifiedClassName));
out.println(jsonSchema);
}
catch (ClassNotFoundException cnfEx)
{
err.println("Unable to find class " + fullyQualifiedClassName);
}
catch (JsonMappingException jsonEx)
{
err.println("Unable to map JSON: " + jsonEx);
}
}
上面清单中的代码实例化获取提供的Java类的类定义(在我的示例中由JAXB xjc编译器生成的最高级别的Food类),并将对该JAXB生成的类的引用传递给ObjectMapper的generateJsonSchema(Class < ?>)方法。 不推荐使用的JsonSchema类的toString()实现非常有用,并且可以轻松写出从JAXB生成的类生成的JSON。
为了进行演示,我将演示驱动程序作为main(String [])函数提供。 在下一个代码清单中提供了该函数以及到目前为止的整个类(包括上面显示的方法)。
JsonGenerationFromJaxbClasses.java,版本1
package dustin.examples.jackson;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import com.fasterxml.jackson.databind.jsonschema.JsonSchema;
import static java.lang.System.out;
import static java.lang.System.err;
/**
* Generates JavaScript Object Notation (JSON) from Java classes
* with Java API for XML Binding (JAXB) annotations.
*/
public class JsonGenerationFromJaxbClasses
{
/**
* Create instance of ObjectMapper with JAXB introspector
* and default type factory.
*
* @return Instance of ObjectMapper with JAXB introspector
* and default type factory.
*/
private ObjectMapper createJaxbObjectMapper()
{
final ObjectMapper mapper = new ObjectMapper();
final TypeFactory typeFactory = TypeFactory.defaultInstance();
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(typeFactory);
// make deserializer use JAXB annotations (only)
mapper.getDeserializationConfig().with(introspector);
// make serializer use JAXB annotations (only)
mapper.getSerializationConfig().with(introspector);
return mapper;
}
/**
* Write out JSON Schema based upon Java source code in
* class whose fully qualified package and class name have
* been provided.
*
* @param mapper Instance of ObjectMapper from which to
* invoke JSON schema generation.
* @param fullyQualifiedClassName Name of Java class upon
* which JSON Schema will be extracted.
*/
private void writeToStandardOutputWithDeprecatedJsonSchema(
final ObjectMapper mapper, final String fullyQualifiedClassName)
{
try
{
final JsonSchema jsonSchema = mapper.generateJsonSchema(Class.forName(fullyQualifiedClassName));
out.println(jsonSchema);
}
catch (ClassNotFoundException cnfEx)
{
err.println("Unable to find class " + fullyQualifiedClassName);
}
catch (JsonMappingException jsonEx)
{
err.println("Unable to map JSON: " + jsonEx);
}
}
/**
* Accepts the fully qualified (full package) name of a
* Java class with JAXB annotations that will be used to
* generate a JSON schema.
*
* @param arguments One argument expected: fully qualified
* package and class name of Java class with JAXB
* annotations.
*/
public static void main(final String[] arguments)
{
if (arguments.length < 1)
{
err.println("Need to provide the fully qualified name of the highest-level Java class with JAXB annotations.");
System.exit(-1);
}
final JsonGenerationFromJaxbClasses instance = new JsonGenerationFromJaxbClasses();
final String fullyQualifiedClassName = arguments[0];
final ObjectMapper objectMapper = instance.createJaxbObjectMapper();
instance.writeToStandardOutputWithDeprecatedJsonSchema(objectMapper, fullyQualifiedClassName);
}
}
要针对基于Food.xsd的JAXB的xjc生成的Java类运行此相对通用的代码,我需要提供最高级别生成的类的标准软件包名称和类名称。 在这种情况下,这就是com.blogspot.marxsoftware.foodxml.Food (程序包名称基于XSD的名称空间,因为在运行xjc时我没有明确覆盖它)。 当我使用完全限定的类名以及类路径上的JAXB类和Jackson库运行上述代码时,我看到以下JSON写入了标准输出。
生成的JSON
{"type":"object","properties":{"vegetable":{"type":"string","enum":["CARROT","SQUASH","SPINACH","CELERY"]},"fruit":{"type":"string"},"dessert":{"type":"string","enum":["PIE","CAKE","ICE_CREAM"]}}}
人类(包括许多开发人员)比打印的JSON更喜欢漂亮的打印 。 我们可以调整演示类的方法writeToStandardOutputWithDeprecatedJsonSchema(ObjectMapper, String) ,如下所示,以写出缩进的JSON ,以更好地反映其层次结构性质。 接下来显示此修改的方法。
修改了writeToStandardOutputWithDeprecatedJsonSchema(ObjectMapper,String)以写入缩进的JSON
/**
* Write out indented JSON Schema based upon Java source
* code in class whose fully qualified package and class
* name have been provided.
*
* @param mapper Instance of ObjectMapper from which to
* invoke JSON schema generation.
* @param fullyQualifiedClassName Name of Java class upon
* which JSON Schema will be extracted.
*/
private void writeToStandardOutputWithDeprecatedJsonSchema(
final ObjectMapper mapper, final String fullyQualifiedClassName)
{
try
{
final JsonSchema jsonSchema = mapper.generateJsonSchema(Class.forName(fullyQualifiedClassName));
out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema));
}
catch (ClassNotFoundException cnfEx)
{
err.println("Unable to find class " + fullyQualifiedClassName);
}
catch (JsonMappingException jsonEx)
{
err.println("Unable to map JSON: " + jsonEx);
}
catch (JsonProcessingException jsonEx)
{
err.println("Unable to process JSON: " + jsonEx);
}
}
当我使用此修改后的方法再次运行演示类时,JSON输出在美学上更加令人愉悦:
生成带有缩进通信层次结构的JSON
{
"type" : "object",
"properties" : {
"vegetable" : {
"type" : "string",
"enum" : [ "CARROT", "SQUASH", "SPINACH", "CELERY" ]
},
"fruit" : {
"type" : "string"
},
"dessert" : {
"type" : "string",
"enum" : [ "PIE", "CAKE", "ICE_CREAM" ]
}
}
}
我在这篇文章中一直在使用Jackson 2.5.4。 com.fasterxml.jackson. databind .jsonschema.JsonSchema类com.fasterxml.jackson. databind .jsonschema.JsonSchema 该版本不建议使用com.fasterxml.jackson. databind .jsonschema.JsonSchema ,并带有以**释:“从2.2开始,我们建议使用外部JSON Schema生成器模块 。” 鉴于此,我现在看一下使用新的首选方法( Jackson JSON Schema Module方法)。
最显著的变化是使用JsonSchema在类com.fasterxml.jackson.module.jsonSchema包,而不是使用JsonSchema在类com.fasterxml.jackson.databind.jsonschema包。 获取这些不同版本的JsonSchema类的实例的方法也不同。 下一个代码清单演示了如何使用更新的首选方法从Java类生成JSON。
使用Jackson的更新和首选com.fasterxml.jackson.module.jsonSchema.JsonSchema
/**
* Write out JSON Schema based upon Java source code in
* class whose fully qualified package and class name have
* been provided. This method uses the newer module JsonSchema
* class that replaces the deprecated databind JsonSchema.
*
* @param fullyQualifiedClassName Name of Java class upon
* which JSON Schema will be extracted.
*/
private void writeToStandardOutputWithModuleJsonSchema(
final String fullyQualifiedClassName)
{
final SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
final ObjectMapper mapper = new ObjectMapper();
try
{
mapper.acceptJsonFormatVisitor(mapper.constructType(Class.forName(fullyQualifiedClassName)), visitor);
final com.fasterxml.jackson.module.jsonSchema.JsonSchema jsonSchema = visitor.finalSchema();
out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema));
}
catch (ClassNotFoundException cnfEx)
{
err.println("Unable to find class " + fullyQualifiedClassName);
}
catch (JsonMappingException jsonEx)
{
err.println("Unable to map JSON: " + jsonEx);
}
catch (JsonProcessingException jsonEx)
{
err.println("Unable to process JSON: " + jsonEx);
}
}
下表将两个Jackson JsonSchema类的用法与左侧较早显示的已弃用方法进行了比较(为该比较进行了一些调整),并在右侧推荐了较新的方法。 两者针对要从其编写JSON的相同给定Java类生成相同的输出。
/**
* Write out JSON Schema based upon Java source code in
* class whose fully qualified package and class name have
* been provided. This method uses the deprecated JsonSchema
* class in the "databind.jsonschema" package
* {@see com.fasterxml.jackson.databind.jsonschema}.
*
* @param fullyQualifiedClassName Name of Java class upon
* which JSON Schema will be extracted.
*/
private void writeToStandardOutputWithDeprecatedDatabindJsonSchema(
final String fullyQualifiedClassName)
{
final ObjectMapper mapper = new ObjectMapper();
try
{
final com.fasterxml.jackson.databind.jsonschema.JsonSchema jsonSchema =
mapper.generateJsonSchema(Class.forName(fullyQualifiedClassName));
out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema));
}
catch (ClassNotFoundException cnfEx)
{
err.println("Unable to find class " + fullyQualifiedClassName);
}
catch (JsonMappingException jsonEx)
{
err.println("Unable to map JSON: " + jsonEx);
}
catch (JsonProcessingException jsonEx)
{
err.println("Unable to process JSON: " + jsonEx);
}
}
/**
* Write out JSON Schema based upon Java source code in
* class whose fully qualified package and class name have
* been provided. This method uses the newer module JsonSchema
* class that replaces the deprecated databind JsonSchema.
*
* @param fullyQualifiedClassName Name of Java class upon
* which JSON Schema will be extracted.
*/
private void writeToStandardOutputWithModuleJsonSchema(
final String fullyQualifiedClassName)
{
final SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
final ObjectMapper mapper = new ObjectMapper();
try
{
mapper.acceptJsonFormatVisitor(mapper.constructType(Class.forName(fullyQualifiedClassName)), visitor);
final com.fasterxml.jackson.module.jsonSchema.JsonSchema jsonSchema = visitor.finalSchema();
out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema));
}
catch (ClassNotFoundException cnfEx)
{
err.println("Unable to find class " + fullyQualifiedClassName);
}
catch (JsonMappingException jsonEx)
{
err.println("Unable to map JSON: " + jsonEx);
}
catch (JsonProcessingException jsonEx)
{
err.println("Unable to process JSON: " + jsonEx);
}
}
这篇博客文章展示了两种方法,这些方法使用由Jackson提供的名称为JsonSchema的类的不同版本来基于从JAXB的xjc的XSD生成的Java类编写JSON。 本文中演示的整个过程是一种从XML Schema生成JSON Schema的方法。
翻译自: https://www.javacodegeeks.com/2015/06/generating-json-schema-from-xsd-with-jaxb-and-jackson.html