• 2013-03-18

    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相等,它们就是相等的。

    分享到:

    历史上的今天:

    招聘杂感 2006-03-18
    震撼重临 2005-03-18
    引用地址: