Feature: JSON Views

Idea - 思路

"Views" in this context mean ability to define subsets of logical properties (things accessed via getters or fields) to serialize. Views are defined statically (using annotations), but view to use for serialization is chosen dynamically (per serialization).

“Views”,在这个语境中,意思是为将要序列化的逻辑属性(通过get方法或者字段获取到的)定义一个子集。views是通过注解(annotations)来静态定义的,但是使用的时候是动态选择的(每个需要序列化的地方自己选择使用哪些views)

Design - 设计

Views themselves are identified by using Classes, instead of Strings (or dedicated objects). Reasons:

views是使用类来定义的,而不是使用字符串,或者其它某些特定的实例。原因是:

With classes, can use inheritance to naturally (?) represent View hierarchies (if a field is part of a View, it'll be also part of parent view)

使用类,就可以使用继承来更加自然的展示views的结构(如果一个字段是某个view的一部分,那么它也是父级view的一部分)

Classes can be used as annotation values: Enums not (Enums would have been one other obvious possibility)

类可以用作注解中的值,而枚举不可以(枚举也是另一个可能用作view的设计)

For future extensibility, classes can also be annotated if need be (no plans for such annotations yet)

考虑未来的扩展性,如果有必要的话,(用作vies定义的)类本身也可以被打上注解(不过不在现有标签的计划中)

View membership defined using annotations: specify which view(s)property will be included in. If no view annotation, assumed to mean View identified by Object.class: that is, included in all views

我们是这样定义视图注解的使用方式的:为某个属性标记上某个注解,标示这个属性将被包含在(最终的json字符串之)内。如果某个属性上没有任何注解,则认为它被标记为“Object.class"的视图,并会被包含在所有视图中。

View to use for serialization (and with  2.0, deserialization) is specified dynamically; active view is a property of  SerializationConfig (and  DeserializationConfig). Conceptually defaults to Object.class; as if no View functionality was used at all.

在序列化为json的过程中(以及,在2.0中,还包括反序列化的过程),view是动态指定的。一个view是一个 SerializationConfig (对反序列化来说则是 DeserializationConfig)的一个属性。这个属性的默认值是Object.class,其功能和不使用view是一样的。

Only single active view per serialization; but due to inheritance of Views, can combine Views via aggregation.

每次序列化都只能使用一个视图;但是,考虑到视图的继承层次,可以把多个视图组合成一个。

All view membership inclusive, no need for exclusions? (however: could add an option to change default handling of 'unmarked' properties to mean "don't include unless specifically identified)

view的用法都是“包含在序列化结果”中的,没有“排除在序列化结果之外”的语义。(不过,可以增加一个选项来改变对“unmarked”属性的默认处理方式——把它改成“除非特别声明,否则不把被标记属性包含在序列化结果中”)

Implementation - 实现

 1.4 implementation is used as follows.

1.4版的实现是这样使用的。

First, defining views means declaring classes; you can reuse existing ones, or just create bogus classes -- they are just view identifiers with relationship information (child inherits view membership from parents):

首先,通过声明一些类,来定义一堆view。你可以重用现有的类或view,或者只创建一些“虚假的类”——所谓“虚假”是指,这些类只用来标识view以及view的依赖和关联(例如,子view会继承父view的信息)。以下是代码:

// View definitions:
class Views {
    static class Public { }
    static class ExtendedPublic extends PublicView { }
    static class Internal extends ExtendedPublicView { }
}

public class Bean {
    // Name is public
    @JsonView(Views.Public.class) String name;
    // Address semi-public
    @JsonView(Views.ExtendPublic.class) Address address;
    // SSN only for internal usage
    @JsonView(Views.Internal.class) SocialSecNumber ssn;
}

With such view definitions, serialization would be done like so:

定义好这些view之后,序列化过程会这样执行:

// short-cut:
objectMapper.writeValueUsingView(out, beanInstance, ViewsPublic.class);

// or fully exploded:
objectMapper.getSerializationConfig().setSerializationView(Views.Public.class);
// (note: can also pre-construct config object with 'mapper.copySerializationConfig'; reuse)
objectMapper.writeValue(out, beanInstance); // will use active view set via Config

// or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);

and result would only contain 'name', not 'address' or 'ssn'.

并且,最终结果会仅仅包含“name”,而没有“address”或“ssn”。

NOTE: even if you only want to use "default" view -- that is, just exclude things that are only to be included in specific "full" view -- you DO need to enable View processing by specifying a view. If you do not have explicit "basic" view setting, just use Object.class.

注意:即使你只想使用“默认”view——也就是说,

后面的就没翻译了……

Handling of "view-less" properties

By default all properties without explicit view definition are included in serialization. But starting with  Jackson 1.5 you can change this default by:

  objectMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);

where false means that such properties are NOT included when enabling with a view. Default for this property is 'true'.

Customization

Although default implementation is not very customizable, underlying design does allow for implementing elaborate custom filtering, using alternative means of defining custom views. Here is how.

Enabling "view" processing

Depending on exactly how custom serialization is implemented, you may (or may not) need to enable view processing. If it is needed, you will just do something like:

  ObjectWriter w = objectMapper.viewWriter(SomeClass.class); // 1.8 and prior
  ObjectWriter w = objectMapper.writerWithView(SomeClass.class); // 1.9 and above

which offers same set of writeValue (and writeValueAsString, writeValueAsBytes, ...) write methods and can be reused easily (or passed).

As of  2.0 this is also available for deserialization, like so:

  ObjectReader r = objectMapper.readerWithView(SomeClass.class); // 2.0 and above

Views with JAX-RS

With  2.3 (of  JAX-RS module) it is also possible to annotate JAX-RS resources like so:

    public class Resource {

      @JsonView(Views.Public.class)
      @GET
      @Produces(MediaType.APPLICATION_JSON )
      public List<Object> getElements() {
        ...
        return someResultList;
      }
    }

so that you need not try to configure active view via ObjectReader / ObjectWriter.

How to plug in custom View processing?

What you need to implement is a sub-class of org.codehaus.jackson.map.ser.BeanPropertyWriter: this is what standard JsonView does; and make SerializerFactory (usually BeanSerializerFactory) construct BeanSerializer with customized versions. Once this is done, you can control output during serialization as you want.

There is some sample code ( src/sample/CustomSerializationView.java) to show how exactly this is done.

Or with  1.7, even better way would be to use  JacksonFeatureJsonFilter, which finally gives full dynamic control for application. Annotation is needed for value types, to indicate logical filter id to use, but mapping from id to filter is fully dynamic (on per-call basis if necessary).

 CategoryJackson


Feature: JSON Views https://blog.51cto.com/winters1224/1668947