Jackson使用非json格式

时间:2022-07-05 18:04:41

I'm using JAX-RS (with RESTEasy) and the latest version of Jackson for marshaling objects in JSON. I simply specify a return content type to be application/json, for example, and my object is converted to JSON. I can use Jackson annotations to tweak which properties are serialized, how they are named, etc.

我正在使用JAX-RS(带有RESTEasy)和最新版本的Jackson来对JSON中的对象进行封送。我只需指定返回内容类型为application/json,例如,我的对象被转换为json。我可以使用Jackson注释来调整哪些属性是序列化的,它们是如何命名的等等。

I have a custom text-based Foo format text/foo (for the sake of discussion) that is similar to JSON but different, especially in the way that objects and properties are delimited. I want to be able to specify text/foo as a return content type and have my object automatically serialized to the Foo format. I want my marshaling library to be independent of the JAX-RS implementation.

我有一个自定义的基于文本的Foo格式文本/ Foo(为了便于讨论),它与JSON类似,但不同,特别是在对象和属性被分隔的方式上。我希望能够指定文本/foo作为返回内容类型,并让我的对象自动序列化为foo格式。我希望我的封送库独立于JAX-RS实现。

My first thought was to leverage Jackson somehow, so that I could make use of Jackson's annotations for property names, etc. That way some object that had been configured for JSON using Jackson would, if I changed the content type to text/foo, be serialized to the Foo format yet still use the property names indicated by the Jackson annotations.

我的第一想法是利用杰克逊,杰克逊注释,这样我就可以利用属性名称,等等这样一些对象被配置为使用杰克逊将JSON,如果我改变了内容类型文本/ foo,被序列化到foo格式但仍使用属性名杰克逊注释所示。

Jackson turns out to be pretty complex, with various APIs, and it's not obvious where to start.

Jackson原来是相当复杂的,有各种各样的api,从哪里开始就不明显了。

  • Is it possible to leverage Jackson to serialize objects to a non-JSON format?
  • 是否可以利用Jackson将对象序列化为非json格式?
  • Which Jackson API (tree model, streaming API, or databinding) should I use?
  • 我应该使用哪个Jackson API(树模型、流API或数据库)?
  • Is there some existing non-JSON Jackson library that would serve as an example to help me get started?
  • 是否有一些现有的非json Jackson库可以作为示例来帮助我入门?
  • Is using Jackson in this way simply more trouble than it's worth, and should I instead just write JAX-RS MessageBodyReader and MessageBodyWriter implementations, skipping Jackson altogether? (I hesitate to go down this road, because I would have to write all the reflection logic from scratch, write my own annotations for specifying property name changes etc., and forgo all the type plugin infrastructure of Jackson.)
  • 用这种方式使用Jackson仅仅是比它的价值更大的麻烦,我应该只写JAX-RS MessageBodyReader和MessageBodyWriter实现,完全跳过Jackson吗?(我不愿沿着这条路走下去,因为我必须从头开始编写所有的反射逻辑,为指定属性名更改编写我自己的注释,以及放弃Jackson的所有类型插件基础设施。)

1 个解决方案

#1


1  

Advantages on Jackson API

优势在杰克逊API

  • Is one of the fastest libraries available to handle with JSON files.
  • 是可用来处理JSON文件的最快的库之一。
  • Has a more extensive annotation support, and even accepts annotations from other APIs like JAXB.
  • 具有更广泛的注释支持,甚至可以接受来自其他api(如JAXB)的注释。
  • Has a better integration with JAX-RS.
  • 与JAX-RS有更好的集成。

Streaming (core)

流(核心)

This is the core of Jackson, it's where the parser/serializer stays, handling with streaming of the input/output data.

这是Jackson的核心,它是解析器/序列化器驻留的地方,处理输入/输出数据的流。

  • This is the fastest way available for parsing/serializing data.
  • 这是解析/序列化数据的最快方法。
  • Not the most convenient way to process JSON content.
    • The content must be processed in exact same order as input/output to work, i.e., no random access.
    • 内容必须按照与输入/输出相同的顺序来处理,即。,没有随机存取。
    • More used by middleware and frameworks, but not very common for applications.
    • 中间件和框架更常用,但应用程序不太常见。
  • 处理JSON内容不是最方便的方式。内容必须按照与输入/输出相同的顺序来处理,即。,没有随机存取。中间件和框架更常用,但应用程序不太常见。

This is the API that we must to implement to have a completely functional version of Jackson that supports the data format that we want. It is also the only API implemented by other data formats like XML, Java Properties, YAML, and others.

这是我们必须实现的API,以拥有完全功能的Jackson版本,该版本支持我们想要的数据格式。它也是其他数据格式(如XML、Java Properties、YAML等)实现的唯一API。

DataBinding and TreeModel will be automatically supported after implementing this API.

实现此API后将自动支持数据库和TreeModel。

GSON was also studied along with Jackson, but due its similarity to Jackson and its lack in the performance, Jackson is the main subject for this report.

GSON也和Jackson一起被研究,但是由于与Jackson的相似之处以及在表演上的不足,Jackson是本报告的主要主题。

The difference of performance is due to the way that those APIs handle with data, Jackson was using streams at first, which is a fast low-level approach, and it made of Jackson the fastest API to handle with JSON when it was launched.

性能的不同是由于这些API处理数据的方式,Jackson首先使用流,这是一种快速的低级方法,它是由Jackson创建的最快的API来处理JSON。

And although GSON has also started using this approach to handle with data, Jackson is still the fastest API according to benchmark tests that may be found on the section on references.

虽然GSON也已经开始使用这种方法来处理数据,但是Jackson仍然是最快的API,这可以从引用部分的基准测试中找到。

TreeModel

TreeModel

This is the part of the api that provides a mutable in-memory tree representation of a JSON document.

这是api的一部分,它提供了一个JSON文档的可变内存树表示。

This API is the most flexible when handling with some data format similar to XML.

当处理类似于XML的数据格式时,此API是最灵活的。

DataBinding

数据绑定

This is the part of the api that converts JSON to and from POJOs based either on property accessor conventions or annotations.

这是api的一部分,它根据属性访问或注释将JSON转换为pojo或从pojo转换。

The most convenient way to process JSON content. It allows conversion between JSON data and JAVA objects. Similar to TreeModel, but use Java objects instead of a node-based model.

处理JSON内容最方便的方式。它允许在JSON数据和JAVA对象之间进行转换。类似于TreeModel,但是使用Java对象而不是基于节点的模型。

This API is the most used in terms of applications

这个API是应用程序中最常用的


Notes about Jackson Streaming Implementation

Jackson流实现的说明

As this is the only API that we need to implement, we will give more attention to this one.

由于这是我们需要实现的唯一API,因此我们将更加关注这个API。

The Implementation for Streaming API uses what's called JsonToken to identify the upcoming JSON objects coming in the stream, i.e., the input/output data.

流媒体API的实现使用了JsonToken来标识即将出现在流中的JSON对象,即。输入/输出数据。

The JsonToken enum has the following definitions:

JsonToken enum有以下定义:

NOT_AVAILABLE(null), 
START_OBJECT("{"), 
END_OBJECT("}"), 
START_ARRAY("["), 
END_ARRAY("]"), 
FIELD_NAME(null), 
VALUE_STRING(null), 
VALUE_NUMBER_INT(null), 
VALUE_NUMBER_FLOAT(null), 
VALUE_TRUE("true"), 
VALUE_FALSE("false"), 
VALUE_NULL("null")

These tokens can be retrieved by what's called ReadContext. The ReadContext has some constants to define its own type.

这些令牌可以通过所谓的ReadContext来检索。ReadContext有一些常量来定义它自己的类型。

Constants used internally to represent a JsonStreamContext type.

内部用于表示JsonStreamContext类型的常量。

protected final static int TYPE_ROOT = 0; // Means the root element.
protected final static int TYPE_ARRAY = 1; // Means the array elements.
protected final static int TYPE_OBJECT = 2; // Means the JSON object elements.

Snippet of the constructor methods of ReadContext for the Java Properties data format.

用于Java属性数据格式的ReadContext的构造函数方法的片段。

JPropReadContext extends JsonStreamContext {
    // Static factory method used to create the JPropReadContext instance.
    public static JPropReadContext create(JPropNode root) {
        if (root.isArray()) { // can this ever occur?
                return new ArrayContext(null, root);
        }
        return new ObjectContext(null, root);
    }
    …
    ArrayContext extends JPropReadContext {
        // Constructor for ArrayContext.
        public ArrayContext(JPropReadContext p, JPropNode arrayNode) {
            super(JsonStreamContext.TYPE_ARRAY, p, arrayNode);
            _contents = arrayNode.arrayContents();
            _state = STATE_START;
        }
        …
    }
    …
    ObjectContext extends JPropReadContext {
        // Constructor for ObjectContext.
        public ObjectContext(JPropReadContext p, JPropNode objectNode) {
            super(JsonStreamContext.TYPE_OBJECT, p, objectNode);
            _contents = objectNode.objectContents();
            _state = STATE_START;
        }
        …
    }
    …
}

JPropReadContext merely extends JsonStreamContext, so it needs to pass a constant from JsonStreamContext to it, indicating the type of the context, if it's a root, an array or an object. The constant is a simply int value, so no problem until that.

JPropReadContext仅仅扩展了JsonStreamContext,因此它需要从JsonStreamContext传递一个常量,以指示上下文的类型,如果它是根、数组或对象。常量是一个简单的int值,所以在此之前没有问题。

Here's the same information but now for the XML data format.

这里是相同的信息,但现在是XML数据格式。

…
// Static factory method used to create the XmlReadContext instance of type ROOT.
public static XmlReadContext createRootContext(int lineNr, int colNr) {
    return new XmlReadContext(null, TYPE_ROOT, lineNr, colNr);
}

// Static factory method used to create the XmlReadContext instance of type ARRAY.
public final XmlReadContext createChildArrayContext(int lineNr, int colNr) {
    XmlReadContext ctxt = _child;       
    if (ctxt == null) {
        _child = ctxt = new XmlReadContext(this, TYPE_ARRAY, lineNr, colNr);
        return ctxt;
    }
    ctxt.reset(TYPE_ARRAY, lineNr, colNr);
    return ctxt;
}

// Static factory method used to create the XmlReadContext instance of type OBJECT.
public final XmlReadContext createChildObjectContext(int lineNr, int colNr) {
    XmlReadContext ctxt = _child;
    if (ctxt == null) {
        _child = ctxt = new XmlReadContext(this, TYPE_OBJECT, lineNr, colNr);
        return ctxt;
    }
    ctxt.reset(TYPE_OBJECT, lineNr, colNr);
    return ctxt;
}
…

What the implementations for other data formats for Jackson do is simply use these constants and the JsonToken as if their format was a Json instance, in other words, it fakes their own format to make it look like a Json instance for Jackson, which is not a clean way. For example, they can make use of TYPE_OBJECT to represent something like <element>value<element> or property = "string containing the value of the property.".

Jackson的其他数据格式的实现只是简单地使用这些常量和JsonToken,就好像它们的格式是一个Json实例,换句话说,它伪造自己的格式,使它看起来像Jackson的Json实例,这不是一种干净的方式。例如,可以使用TYPE_OBJECT来表示诸如 value 或property = "string contains the value of the property "之类的内容。

This works for formats that have the same/less amount of features than Json, because every feature can be implemented, but it's impossible for data formats that have more features than that.

这种格式与Json相比具有相同/更少的特性,因为每个特性都可以实现,但是对于具有更多特性的数据格式来说是不可能的。


This report was made for GlobalMentor Inc., as part of https://globalmentor.atlassian.net/browse/URF-33, where we try to implement the support for the SURF data format in Jackson.

这份报告是为GlobalMentor Inc.撰写的,它是https://globalmentor.atlassian.net/browse/URF-33的一部分,我们试图在Jackson中实现对SURF数据格式的支持。

#1


1  

Advantages on Jackson API

优势在杰克逊API

  • Is one of the fastest libraries available to handle with JSON files.
  • 是可用来处理JSON文件的最快的库之一。
  • Has a more extensive annotation support, and even accepts annotations from other APIs like JAXB.
  • 具有更广泛的注释支持,甚至可以接受来自其他api(如JAXB)的注释。
  • Has a better integration with JAX-RS.
  • 与JAX-RS有更好的集成。

Streaming (core)

流(核心)

This is the core of Jackson, it's where the parser/serializer stays, handling with streaming of the input/output data.

这是Jackson的核心,它是解析器/序列化器驻留的地方,处理输入/输出数据的流。

  • This is the fastest way available for parsing/serializing data.
  • 这是解析/序列化数据的最快方法。
  • Not the most convenient way to process JSON content.
    • The content must be processed in exact same order as input/output to work, i.e., no random access.
    • 内容必须按照与输入/输出相同的顺序来处理,即。,没有随机存取。
    • More used by middleware and frameworks, but not very common for applications.
    • 中间件和框架更常用,但应用程序不太常见。
  • 处理JSON内容不是最方便的方式。内容必须按照与输入/输出相同的顺序来处理,即。,没有随机存取。中间件和框架更常用,但应用程序不太常见。

This is the API that we must to implement to have a completely functional version of Jackson that supports the data format that we want. It is also the only API implemented by other data formats like XML, Java Properties, YAML, and others.

这是我们必须实现的API,以拥有完全功能的Jackson版本,该版本支持我们想要的数据格式。它也是其他数据格式(如XML、Java Properties、YAML等)实现的唯一API。

DataBinding and TreeModel will be automatically supported after implementing this API.

实现此API后将自动支持数据库和TreeModel。

GSON was also studied along with Jackson, but due its similarity to Jackson and its lack in the performance, Jackson is the main subject for this report.

GSON也和Jackson一起被研究,但是由于与Jackson的相似之处以及在表演上的不足,Jackson是本报告的主要主题。

The difference of performance is due to the way that those APIs handle with data, Jackson was using streams at first, which is a fast low-level approach, and it made of Jackson the fastest API to handle with JSON when it was launched.

性能的不同是由于这些API处理数据的方式,Jackson首先使用流,这是一种快速的低级方法,它是由Jackson创建的最快的API来处理JSON。

And although GSON has also started using this approach to handle with data, Jackson is still the fastest API according to benchmark tests that may be found on the section on references.

虽然GSON也已经开始使用这种方法来处理数据,但是Jackson仍然是最快的API,这可以从引用部分的基准测试中找到。

TreeModel

TreeModel

This is the part of the api that provides a mutable in-memory tree representation of a JSON document.

这是api的一部分,它提供了一个JSON文档的可变内存树表示。

This API is the most flexible when handling with some data format similar to XML.

当处理类似于XML的数据格式时,此API是最灵活的。

DataBinding

数据绑定

This is the part of the api that converts JSON to and from POJOs based either on property accessor conventions or annotations.

这是api的一部分,它根据属性访问或注释将JSON转换为pojo或从pojo转换。

The most convenient way to process JSON content. It allows conversion between JSON data and JAVA objects. Similar to TreeModel, but use Java objects instead of a node-based model.

处理JSON内容最方便的方式。它允许在JSON数据和JAVA对象之间进行转换。类似于TreeModel,但是使用Java对象而不是基于节点的模型。

This API is the most used in terms of applications

这个API是应用程序中最常用的


Notes about Jackson Streaming Implementation

Jackson流实现的说明

As this is the only API that we need to implement, we will give more attention to this one.

由于这是我们需要实现的唯一API,因此我们将更加关注这个API。

The Implementation for Streaming API uses what's called JsonToken to identify the upcoming JSON objects coming in the stream, i.e., the input/output data.

流媒体API的实现使用了JsonToken来标识即将出现在流中的JSON对象,即。输入/输出数据。

The JsonToken enum has the following definitions:

JsonToken enum有以下定义:

NOT_AVAILABLE(null), 
START_OBJECT("{"), 
END_OBJECT("}"), 
START_ARRAY("["), 
END_ARRAY("]"), 
FIELD_NAME(null), 
VALUE_STRING(null), 
VALUE_NUMBER_INT(null), 
VALUE_NUMBER_FLOAT(null), 
VALUE_TRUE("true"), 
VALUE_FALSE("false"), 
VALUE_NULL("null")

These tokens can be retrieved by what's called ReadContext. The ReadContext has some constants to define its own type.

这些令牌可以通过所谓的ReadContext来检索。ReadContext有一些常量来定义它自己的类型。

Constants used internally to represent a JsonStreamContext type.

内部用于表示JsonStreamContext类型的常量。

protected final static int TYPE_ROOT = 0; // Means the root element.
protected final static int TYPE_ARRAY = 1; // Means the array elements.
protected final static int TYPE_OBJECT = 2; // Means the JSON object elements.

Snippet of the constructor methods of ReadContext for the Java Properties data format.

用于Java属性数据格式的ReadContext的构造函数方法的片段。

JPropReadContext extends JsonStreamContext {
    // Static factory method used to create the JPropReadContext instance.
    public static JPropReadContext create(JPropNode root) {
        if (root.isArray()) { // can this ever occur?
                return new ArrayContext(null, root);
        }
        return new ObjectContext(null, root);
    }
    …
    ArrayContext extends JPropReadContext {
        // Constructor for ArrayContext.
        public ArrayContext(JPropReadContext p, JPropNode arrayNode) {
            super(JsonStreamContext.TYPE_ARRAY, p, arrayNode);
            _contents = arrayNode.arrayContents();
            _state = STATE_START;
        }
        …
    }
    …
    ObjectContext extends JPropReadContext {
        // Constructor for ObjectContext.
        public ObjectContext(JPropReadContext p, JPropNode objectNode) {
            super(JsonStreamContext.TYPE_OBJECT, p, objectNode);
            _contents = objectNode.objectContents();
            _state = STATE_START;
        }
        …
    }
    …
}

JPropReadContext merely extends JsonStreamContext, so it needs to pass a constant from JsonStreamContext to it, indicating the type of the context, if it's a root, an array or an object. The constant is a simply int value, so no problem until that.

JPropReadContext仅仅扩展了JsonStreamContext,因此它需要从JsonStreamContext传递一个常量,以指示上下文的类型,如果它是根、数组或对象。常量是一个简单的int值,所以在此之前没有问题。

Here's the same information but now for the XML data format.

这里是相同的信息,但现在是XML数据格式。

…
// Static factory method used to create the XmlReadContext instance of type ROOT.
public static XmlReadContext createRootContext(int lineNr, int colNr) {
    return new XmlReadContext(null, TYPE_ROOT, lineNr, colNr);
}

// Static factory method used to create the XmlReadContext instance of type ARRAY.
public final XmlReadContext createChildArrayContext(int lineNr, int colNr) {
    XmlReadContext ctxt = _child;       
    if (ctxt == null) {
        _child = ctxt = new XmlReadContext(this, TYPE_ARRAY, lineNr, colNr);
        return ctxt;
    }
    ctxt.reset(TYPE_ARRAY, lineNr, colNr);
    return ctxt;
}

// Static factory method used to create the XmlReadContext instance of type OBJECT.
public final XmlReadContext createChildObjectContext(int lineNr, int colNr) {
    XmlReadContext ctxt = _child;
    if (ctxt == null) {
        _child = ctxt = new XmlReadContext(this, TYPE_OBJECT, lineNr, colNr);
        return ctxt;
    }
    ctxt.reset(TYPE_OBJECT, lineNr, colNr);
    return ctxt;
}
…

What the implementations for other data formats for Jackson do is simply use these constants and the JsonToken as if their format was a Json instance, in other words, it fakes their own format to make it look like a Json instance for Jackson, which is not a clean way. For example, they can make use of TYPE_OBJECT to represent something like <element>value<element> or property = "string containing the value of the property.".

Jackson的其他数据格式的实现只是简单地使用这些常量和JsonToken,就好像它们的格式是一个Json实例,换句话说,它伪造自己的格式,使它看起来像Jackson的Json实例,这不是一种干净的方式。例如,可以使用TYPE_OBJECT来表示诸如 value 或property = "string contains the value of the property "之类的内容。

This works for formats that have the same/less amount of features than Json, because every feature can be implemented, but it's impossible for data formats that have more features than that.

这种格式与Json相比具有相同/更少的特性,因为每个特性都可以实现,但是对于具有更多特性的数据格式来说是不可能的。


This report was made for GlobalMentor Inc., as part of https://globalmentor.atlassian.net/browse/URF-33, where we try to implement the support for the SURF data format in Jackson.

这份报告是为GlobalMentor Inc.撰写的,它是https://globalmentor.atlassian.net/browse/URF-33的一部分,我们试图在Jackson中实现对SURF数据格式的支持。