首页 文章

对Jackson使用非JSON格式

提问于
浏览
2

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

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

我的第一个想法是以某种方式利用 Jackson ,以便我可以利用 Jackson 的注释来获取属性名称等 . 这样,使用 Jackson 为JSON配置的一些对象,如果我将内容类型更改为 text/foo ,则会被序列化为Foo格式仍然使用Jackson注释指示的属性名称 .

Jackson 变得非常复杂,有各种各样的API,而且从哪里开始并不明显 .

  • 是否可以利用Jackson将对象序列化为非JSON格式?

  • 我应该使用哪种Jackson API(树模型,流API或数据绑定)?

  • 是否有一些现有的非JSON Jackson库可以作为帮助我入门的例子?

  • 以这种方式使用 Jackson 只是比它的 Value 更麻烦,我应该只写JAX-RS MessageBodyReaderMessageBodyWriter 实现,完全跳过 Jackson ? (我不愿意走这条路,因为我必须从头开始编写所有反射逻辑,编写我自己的注释以指定属性名称更改等,并放弃Jackson的所有类型插件基础结构 . )

1 回答

  • 1

    Advantages on Jackson API

    • 是可用于处理JSON文件的最快库之一 .

    • 具有更广泛的注释支持,甚至可以接受来自其他API(如JAXB)的注释 .

    • 与JAX-RS有更好的集成 .

    Streaming (core)

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

    • 这是解析/序列化数据的最快方法 .

    • 不是处理JSON内容的最便捷方式 .

    • 必须以与输入/输出完全相同的顺序处理内容才能工作,即无随机访问 .

    • 中间件和框架使用的更多,但对于应用程序来说并不常见 .

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

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

    GSON也与 Jackson 一起学习,但由于其与 Jackson 的相似性及其表现不足, Jackson 是本报告的主要主题 .

    性能的差异是由于这些API处理数据的方式,Jackson首先使用流,这是一种快速的低级方法,并且它是Jackson在启动时使用JSON处理的最快的API .

    虽然GSON也开始使用这种方法处理数据,但 Jackson 仍然是最快的API,根据基准测试可能会在参考部分找到 .

    TreeModel

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

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

    DataBinding

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

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

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


    Notes about Jackson Streaming Implementation

    由于这是我们需要实现的唯一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实例,换句话说,它伪造自己的格式使它看起来像 Jackson 的Json实例,这不是一个干净的方式 . 例如,他们可以使用 TYPE_OBJECT 来表示类似 <element>value<element>property = "string containing the value of the property." 的内容 .

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


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

相关问题