`
zzc1684
  • 浏览: 1228005 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

Jackson雕虫技

阅读更多

Jackson雕虫技(一)

Tag:jackson

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://www.blogbus.com/dreamhead-logs/230244031.html

Jackson是一个高性能JSON处理器,堪称目前Java世界处理JSON的首选。在Moco里,JSON文件的处理就是用它来做的。下面就以Moco处理中用到几点技巧介绍一下Jackson。

读取对象

采用Jackson的databind包,可以很容易地将一个JSON流映射为一个Java对象。下面是一个JSON流:

{
  "request": {
    "text": "foo"
  },
  "response": {
    "text": "bar"
  }
}

我们要把它映射为下面的对象:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class SessionSetting {
  private RequestSetting request;
  private ResponseSetting response;
  ...
}

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class RequestSetting {
  private String text;
  ...
}

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class ResponseSetting {
 private String text;
  ...
}

通过下面这段代码就可以完成映射:

  ObjectMapper mapper = new ObjectMapper();
  SessionSetting setting = mapper.readValue(inputStream, SessionSetting.class);

上面代码中的映射几乎一对一的,很好理解,唯一需要解释是每个类上的声明

  @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)

这声明了字段可见性,如果不做任何声明,缺省情况下需要给每个字段增加一个getter,才能完成映射。

读取集合

目前Moco配置文件里实际上是一组Session配置,也就是一个JSON数组对象:

[
{
  "request": {
    "text": "foo"
  },
  "response": {
    "text": "bar"
  }
}
]

众所周知,JVM的范型是假范型,所以,下面的代码不起作用

  mapper.readValue(inputStream, List.class);

当然,Jackson给我们提供了其它的方式读取集合,下面就是:

  TypeFactory factory = TypeFactory.defaultInstance();
  mapper.readValue(is, factory.constructCollectionType(List.class, SessionSetting.class));

忽略字段

在Moco的配置文件里,我们可以用description描述一个配置的作用,其实,它就是一个注释,在实际的代码里没有任何作用。我们当然像下面这么做,把它读到内存里:

public class SessionSetting {
  private String description;
  ...
}

但Jackson给我们提供了更好的做法:

@JsonIgnoreProperties({ "description" })
public class SessionSetting {
  ...
}

这样一来,连运行时的内存占用都省了。

 

Jackson雕虫技(二)

Tag:jackson

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://www.blogbus.com/dreamhead-logs/230244140.html

定制解析

前面我们看到了,在Moco配置文件里,我们可以支持text匹配,但是,目前Moco还支持下面这种结构:

{
  "request": {
    "text": {
      "match": "foo\\w*bar"
    }
  },
  "response": {
    "text": "match"
  }
}

这里的做法是支持正则表达式的匹配,因为text后面跟的是一个对象,不再是一个字符串,所以,原来声明为String的做法就不起作用了。Moco目前的解决方式是采用了自定义解析的方式,在Jackson里,称之为JsonDeserializer。

首先,我们定义对象,包含对直接字符串和带操作符对象的支持:

public class TextContainer {
    private String text;
    private String operation;
    ...
}

然后,为其定义一个JsonDeserializer,专用于解析TextContainer,代码如下:

public class TextContainerDeserializer extends JsonDeserializer {
    @Override

    public TextContainer deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonToken currentToken = jp.getCurrentToken();
        if (currentToken == JsonToken.VALUE_STRING) {
            return new TextContainer(jp.getText().trim(), null);
        } else if (currentToken == JsonToken.START_OBJECT) {
            JsonToken jsonToken = jp.nextToken();
            if (jsonToken == JsonToken.FIELD_NAME) {
                String operation = jp.getText().trim();
                jp.nextToken();
                String text = jp.getText().trim();
                jp.nextToken();
                return new TextContainer(text, operation);
            }
        }

        throw ctxt.mappingException(TextContainer.class, currentToken);
    }
}

如果你熟悉解析器的一点技巧,便不难理解,我们得到了一个个Token,然后,根据自己需要进行解析。实际上,我们就是参与到解析的过程中去了。从 这段代码,可以看到,如果是一个字符串(JsonToken.VALUE_STRING),则TextContainer里的operation字段为 null,如果是一个对象(JsonToken.START_OBJECT),则要把字段(JsonToken.FIELD_NAME)内文本和操作都解 析出来。这里唯一需要提醒的点是最后一个jp.nextToken(),从这段代码本身它并没有任何意义,实际上,它处理的是 END_OBJECT(“}”)。在Jackson的处理中,每一个JsonDeserializer都要把自己涉及到的Token用光,否则会影响进一 步处理的。

有了JsonDeserializer,接下来就是使用它了。我们可以在字段上使用Annotation进行标记。

public class RequestSetting {
    @JsonDeserialize(using = TextContainerDeserializer.class)
    private TextContainer text;
    ...
}

如果我们只有一个字段用到这个类型,这种做法或许是可以接受的。但如果有大量字段用这个类型,我们就要用一种全局的方式来处理了。在Jackson里,这种方式成为Module:

  Module textContainerModule = new SimpleModule("TextContainerModule",
    new Version(1, 0, 0, null, null, null))
    .addDeserializer(TextContainer.class, new TextContainerDeserializer());
  mapper.registerModule(textContainerModule);

如此一来,只要是TextContainer类型,就会使用我们的TextContainerDeserializer进行处理了,省去了一处一处配置的繁琐。

比较JSON

Moco里有一个操作符,用以比较请求是否是预期的JSON请求。由于空格、Tab、回车换行等字符的存在,两个JSON对象是否相等,不能仅靠字 符串的比较。一种比较方案是,将两个流都读成内存对象进行比较,类似于XML中的DOM。在Jackson里,读出的是一棵树:

  JsonNode requestNode = mapper.readTree(requestStream);
  JsonNode resourceNode = mapper.readTree(targetStream);
  return requestNode.equals(resourceNode);

在这段代码里,只要两个JsonNode相等,它们就是相等的。

 

Jackson雕虫技(三)

Tag:jackson

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://www.blogbus.com/dreamhead-logs/258185547.html

Jackson雕虫技(一)
Jackson雕虫技(二)

使用Builder模式

在日常开发中,我们希望自己编写的类尽可能不变的,对于参数比较多的类,我们通常采用的方法是Builder模式。但如果我们使用Builder模式构造这样的不变对象,当我们将json反序列化成Java对象该怎么办呢?

Jackson已经为这种做法做好了准备,我们可以告诉它这个类是采用Builder模式构建的:

@JsonDeserialize(builder = DefaultHttpRequest.Builder.class)
public class DefaultHttpRequest implements HttpRequest {
 ...
}

我们使用了一个Annotaiton:@JsonDeserialize,通过builder参数告诉它,用哪个类做Builder。这里用的就是这个类的一个内嵌类:Builder。

public class DefaultHttpRequest implements HttpRequest {
   ...

  public static final class Builder {

    ...

    public Builder withVersion(String version) {
      this.version = version;
      return this;
   }

    ...

    public DefaultHttpRequest build() {
      ...
      return request;
   }
  }
}

其中,以with开头的就是用来传参数的方法,而build方法则用以构建最终的对象,这是一个缺省的约定。我们还可以按照自己的需要进行订制,只不过要给我们的Builder加上另外一个Annotation:@JsonPOJOBuilder。下面是一个例子:

@JsonPOJOBuilder(buildMethodName="create", withPrefix="con")
public static final class Builder {
   ...
}

这样一来,所有传参的方法都是以con开头,而构建对象的方法名则改成了create。

使用对象简化解析

在《Jackson雕虫技(二)》,我们提到了可以用自定义解析的方式解析对象,但一个一个字段解析写起来并不直观。其实,我们还可以借用已有的对象解析机制简化这个过程。下面是MocoProxyContainerDeserializer,它根据当前正在解析的目标进行处理,要么解析成一个URL,要么解析成一个Proxy的配置。

public class ProxyContainerDeserializer extends JsonDeserializer {
   @Override
   public ProxyContainer deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
    JsonToken currentToken = jp.getCurrentToken();
     if (currentToken == JsonToken.VALUE_STRING) {
      return builder().withUrl(jp.getText().trim()).build();
    } else if (currentToken == JsonToken.START_OBJECT) {
      InternalProxyContainer container = get(jp.readValuesAs(InternalProxyContainer.class), 0);
      return container.toProxyContainer();
   }

    throw ctxt.mappingException(TextContainer.class, currentToken);
  }
}

这里的关键是readValuesAs,我们没有直接解析接下来的Token,而是确定解析目标之后,又借用了解析器本身的能力,把它解析成一个对象。至于InternalProxyContainer,它只是一个简单的对象类,用以装载解析的结果。

private static class InternalProxyContainer {
  public String url;
  public String from;
  public String to;
  public String failover;
  public String playback;

  public ProxyContainer toProxyContainer() {
    ...
  }
}

 

Jackson雕虫技(四)

Tag:jackson

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://www.blogbus.com/dreamhead-logs/270174350.html

Jackson雕虫技(一)
Jackson雕虫技(二)
Jackson雕虫技(三)

忽略空字段

有时候,我们返回对象的字段可能会有为空的情况。缺省情况下,这些字段会以null的形式呈现出来。但如果我们希望忽略这些字段该如何处理呢?如果是针对某个具体的类,我们可以使用JsonInclude这个annotation。

@JsonInclude(JsonInclude.Include.NON_NULL)
class Entity {
   ...
}

如果所有类都能够遵循这样的规则,那就要在全局配置了,配在Object Mapper上:

  mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

Joda-Time

在《你应该更新的Java知识》里面,我推荐大家在Java 8之前使用Joda Time,而非JDK原生的Date和Calendar。但是,如果我们使用了Joda Time,到了Jackson该怎么办呢?

Java对象和JSON的相互转换,只是一个序列化和反序列化的过程。我们在之前的雕虫技中已经见识过如何针对自己的类自定义序列化和反序列化。所 以,本质上来说,为Joda Time自定义一套,自然也不是什么难事。不过,通用如Joda Time这样的程序库,自然也应该有现成的支持。我们直接拿过来用就好了。

实际上,Jackson已经为Joda Time提供了官方支持。如果你和我一样喜欢Gradle,下面就是依赖的添加方式,其中,jacksonVersion自然是Jackson的版本号。

  "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jacksonVersion"

有了依赖,我们只要在Object Mapper上注册一个模块即可。

  mapper.registerModule(new JodaModule());

现在就可以自己的对象里使用DateTime、LocalDate、LocalTime这样的类型了,有了JodaModule,Jackson就会为我们照顾好这些类型的转换。

我们习惯的日期表示方式一般是年月日时分秒,但Joda Time缺省的做法却是一个数字,一个以毫秒为单位计算出的数字,它叫时间戳。如果你希望让它变成我们喜欢的样子,可以这样做:

  mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

如果你还让自己的日期表现方式中加入时区,那就再加上时区:

  mapper.setTimeZone(TimeZone.getDefault());

Guava

同样在《你应该更新的Java知识》,我还提到一个观点,只要是Java项目就应该使用Guava。我也展示过Guava中一些库的用法。我现在已 经无可救药地爱上了Guava中的不变集合。但同Joda Time一样,Guava不是标准的JDK的一部分,也需要额外的支持。值得高兴的是,这个支持也有现成的,也来自Jackson的官方,依赖如下:

  "com.fasterxml.jackson.datatype:jackson-datatype-guava:$jacksonVersion"

同样,它也要Object Mapper上注册一个模块。

  mapper.registerModule(new GuavaModule());

如此一来,Jackson就可以支持Guava提供的大部分内容,包括Optional和一些不变集合,以及一些新增的集合。

分享到:
评论

相关推荐

    jackson-annotations、jackson-core、jackson-databind

    "jackson-annotations"、"jackson-core"和"jackson-databind"是Jackson库的三个核心模块,它们协同工作,提供了强大的JSON序列化和反序列化功能。 1. **jackson-annotations**:这个模块包含了各种注解,如`@...

    jackson-annotations jackson-core jackson-databind

    标题提到的"jackson-annotations"、"jackson-core"和"jackson-databind"是Jackson库的核心组成部分,它们协同工作,使得Java对象能够被序列化为JSON,以及反序列化为Java对象。 1. **jackson-annotations**:这个...

    jackson-core所需的三个包;jackson-databind.jar,jackson-annotations.jar,jackson-core.jar

    Jackson是Java领域中广泛使用的JSON处理库,它提供了一套高效、灵活的API来解析、生成、转换JSON数据。在Spring MVC框架中,Jackson库通常用于处理HTTP请求和响应中的JSON数据,使得Java对象能够方便地序列化为JSON...

    jackson-databind jackson-annatation jackson-core jackson-mapper

    Jackson是Java领域中广泛使用的JSON处理库,由FasterXML公司开发并维护。它提供了高效、功能丰富的处理JSON的方式,包括序列化和反序列化。在这个包中提到了四个关键组件:"jackson-databind"、"jackson-annotations...

    jackson-annotations.jar jackson-core.jar jackson-databind.jar

    Jackson是Java领域中广泛使用的JSON处理库,由FasterXML公司开发并维护。这个库的主要功能是将Java对象转换为JSON格式,同时也能够将JSON数据转换回Java对象,极大地简化了JSON的序列化和反序列化操作。在给定的...

    jackson-annotations-2.9.5.jar ,jackson-core-2.9.5.jar,jackson-databind-2.9.5.jar

    这个压缩包包含的三个核心组件——`jackson-annotations-2.9.5.jar`、`jackson-core-2.9.5.jar`和`jackson-databind-2.9.5.jar`是Jackson库的核心部分,它们协同工作,提供了全面的JSON解析和生成能力。 1. **...

    Jackson 2.9.3 和 jackson 2.9.3源码

    Jackson是Java领域中广泛使用的JSON处理库,2.9.3是其版本号。这个压缩包包含两个主要文件:`jackson-core-2.9.3.jar`和`jackson-core-master(1).zip`。前者是编译后的二进制JAR文件,可以直接在项目中引用以使用...

    jackson 全部jar包

    Jackson是Java领域中广泛使用的JSON处理库,由FasterXML公司开发并维护。它提供了高效、功能丰富的处理JSON的方式,包括序列化、反序列化以及数据绑定。在JSON的解析和生成方面,Jackson具有高性能、易用性以及灵活...

    jackson-annotations-2.2.3.jar jackson-core-2.2.3.jar jackson-databind-2.2.3.jar

    这个压缩包包含了Jackson库的三个核心组件的2.2.3版本:`jackson-annotations-2.2.3.jar`、`jackson-core-2.2.3.jar` 和 `jackson-databind-2.2.3.jar`。 1. **jackson-annotations-2.2.3.jar**: 这个模块提供了...

    jackson2.7-jar

    Jackson是Java领域中广泛使用的JSON处理库,它的2.7版本是其发展中的一个重要里程碑,提供了许多增强特性和性能优化。Jackson 2.7 jar文件是这个版本的二进制库,包含了处理JSON序列化和反序列化所需的所有类和方法...

    jackson依赖jar包.

    Jackson是Java领域中广泛使用的JSON处理库,由FasterXML公司开发并维护。它提供了高效、功能丰富的处理JSON的各种工具,包括序列化和反序列化。在Java应用程序中,Jackson库是一个不可或缺的部分,特别是对于那些...

    jackson 1.5中文帮助文档

    Jackson是Java领域中广泛使用的JSON处理库,以其高效性能和易用性著称。1.5版本虽然相对较老,但其核心概念和功能对于理解JSON序列化和反序列化仍具有重要价值。以下是对Jackson 1.5中文帮助文档中的关键知识点的...

    jackson-2.7.0版架包

    jackson-2.7.0版架包jackson-2.7.0版架包jackson-2.7.0版架包jackson-2.7.0版架包jackson-2.7.0版架包jackson-2.7.0版架包jackson-2.7.0版架包jackson-2.7.0版架包jackson-2.7.0版架包jackson-2.7.0版架包jackson-...

    jackson-all.jar

    jackson优势: (1)Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象。 (2)相比json-lib框架,Jackson所依赖的jar包较少,只需下载:jackson-all-1.6.*.jar ~ jackson-all-...

    jackson升级jar包2.12.4版本

    在Java开发中,Jackson库是广泛使用的JSON处理库,它提供了高效的序列化和反序列化功能。然而,随着时间的推移,Jackson库也可能暴露出安全问题。标题提到的"Jackson-databind任意文件读取漏洞"就是一个典型的例子,...

    jackson-core-asl-1.9.13+jackson-mapper-asl-1.9.13札包

    在这个"jackson-core-asl-1.9.13+jackson-mapper-asl-1.9.13"压缩包中,包含的是Jackson库的两个核心组件:`jackson-core-asl-1.9.13.jar`和`jackson-mapper-asl-1.9.13.jar`。 `jackson-core-asl-1.9.13.jar`是...

    jackson 完整Jar包

    Jackson fasterxml和codehaus的区别: 他们是Jackson的两大分支、也是两个版本的不同包名。Jackson从2.0开始改用新的包名fasterxml;1.x版本的包名是codehaus。除了包名不同,他们的Maven artifact id也不同。1.x...

    jackson-2.8.11.jar

    它的核心组件包括三个主要部分:jackson-annotations、jackson-core和jackson-databind,这恰好对应了给定的文件名"jackson-annotations-2.8.11.jar"、"jackson-core-2.8.11"和"jackson-databind-2.8.11.jar"。...

    jackson-annotations-2.2.3.jar(Jackson解析需引入的包)

    Jackson是Java领域中广泛使用的JSON处理库,由FasterXML公司开发并维护。该库提供了一整套解决方案,包括序列化、反序列化以及数据绑定等功能,使得在Java应用程序中处理JSON数据变得非常便捷。标题提到的"jackson-...

    jackson-databind-2.9.10.8.jar升级相关jar包

    jackson-databind-2.9.10.8.jar升级相关jar包包含: jackson-module-jaxb-annotations-2.9.10.jar jackson-core-2.9.10.jar jackson-databind-2.9.10.8.jar jackson-annotations-2.9.10.jar jackson-jaxrs-json-...

Global site tag (gtag.js) - Google Analytics