对Jackson使用非JSON格式

时间:2017-07-21 16:45:11

标签: java json jackson jax-rs

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

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

我的第一个想法是以某种方式利用杰克逊,以便我可以利用杰克逊对属性名称等的注释。这样,如果我改变了内容,那么使用杰克逊为JSON配置的一些对象就会键入text/foo,序列化为Foo格式,但仍然使用Jackson注释指示的属性名称。

杰克逊证明是相当复杂的,有各种各样的API,并且从哪里开始并不明显。

  • 是否可以利用Jackson将对象序列化为非JSON格式?
  • 我应该使用哪种Jackson API(树模型,流API或数据绑定)?
  • 是否有一些现有的非JSON Jackson库可以作为帮助我入门的例子?
  • 以这种方式使用Jackson只是比它的价值更麻烦,而且我应该只编写JAX-RS MessageBodyReaderMessageBodyWriter实现,完全跳过Jackson? (我不愿意走这条路,因为我必须从头开始编写所有反射逻辑,编写我自己的注释以指定属性名称更改等,并放弃Jackson的所有类型插件基础结构。)

1 个答案:

答案 0 :(得分:1)

Jackson API的优势

  • 是可用于处理JSON文件的最快库之一。
  • 具有更广泛的注释支持,甚至可以接受来自其他API(如JAXB)的注释。
  • 与JAX-RS有更好的集成。

流媒体(核心)

这是Jackson的核心,它是解析器/序列化器所在的位置,处理输入/输出数据的流式传输。

  • 这是解析/序列化数据的最快方法。
  • 不是处理JSON内容的最便捷方式。
    • 必须以与输入/输出完全相同的顺序处理内容才能工作,即无随机访问。
    • 中间件和框架使用的更多,但对于应用程序来说并不常见。

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

实施此API后,将自动支持DataBinding和TreeModel。

GSON也与杰克逊一起学习,但由于杰克逊与杰克逊的相似性及其缺乏表现,杰克逊是本报告的主题。

性能的差异是由于这些API处理数据的方式,Jackson首先使用流,这是一种快速的低级方法,它使Jackson成为使用JSON处理的最快的API什么时候推出。

尽管GSON也开始使用这种方法来处理数据,但杰克逊仍然是最快的API,根据基准测试可能会在参考部分找到。

<强>的TreeModel

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

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

<强>数据绑定

这是api的一部分,它根据属性访问器约定或注释将POSON转换为POJO和从POJO转换。

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

此API在应用程序方面最常用

关于杰克逊流媒体实施的说明

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

Streaming API的实现使用所谓的JsonToken来识别流中即将出现的JSON对象,即输入/输出数据。

JsonToken枚举具有以下定义:

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")

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

内部使用的常量表示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.

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仅扩展JsonStreamContext,因此它需要从JsonStreamContext传递一个常量,指示上下文的类型,如果它是根,数组或对象。常量是一个简单的int值,所以在那之前没问题。

以下是相同的信息,但现在是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;
}
…

Jackson的其他数据格式的实现只是使用这些常量和JsonToken,就好像它们的格式是Json实例一样,换句话说,它伪造自己的格式使其看起来像Json杰克逊的例子,这不是一个干净的方式。例如,他们可以使用TYPE_OBJECT来表示<element>value<element>property = "string containing the value of the property."之类的内容。

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

此报告是作为https://globalmentor.atlassian.net/browse/URF-33的一部分为GlobalMentor Inc.制作的,我们尝试在杰克逊中实施对SURF data format的支持。