Java序列化篇之Json解析工具Jackson

json 的解析包:

<!--核心类 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.6</version>
</dependency>
<!--注解-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.6</version>
</dependency>
<!--数据绑定-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.6</version>
</dependency>

Jackson的基本应用:

JsonNode 类是 Jackson 的一个将 Json 内容反序列到内存的一种内存数据结构表达方式。
例如对于一个测试的Json内容:

{
  "name" : "test",
  "value" : 20,
  "address" : [
    {
     "addressId" : "0001" 
    },
    {
    "addressId" : "0002"
    }
  ]
}

当我们把json内容反序列化到内存中的时候,便可以如同操作一个树结构读取json内容:

String name = node.get("name").asText();
Integer value = node.get("value").asInt();
Iterator<JsonNode> addressNodes = node.get("address").elements();
String address = addressNodes.next().get("addressId").asText();

从上我们可以发现, jackson 可以方便的进行节点定位跟类型转换。

如何才能将json内容转换成 JsonNode 呢 ?

  • 文件字符串解析
public static JsonNode str2JsonNode(String str) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readTree(str);
}

  • stream 解析
public static JsonNode str2JsonNode(InputStream stream) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readTree(stream);
}
  • file 解析
public static JsonNode str2JsonNode(File file) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readTree(file);
}
  • reader 解析
public static JsonNode str2JsonNode(Reader reader) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readTree(reader);
}
  • bytes 解析
public static JsonNode str2JsonNode(byte[] datas) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readTree(datas);
}

Json 与 基本类型的相互转化:

readValue 方法能够帮助我们将字符串序列化成指定的

 public static Object json2Object(String str) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    JsonNode node = objectMapper.readTree(str);
    if(node.isObject()){
        return objectMapper.readValue(str, Map.class);
    }else if(node.isArray()){
        return objectMapper.readValue(str, List.class);
    }else{
        if (node.isInt()) {
            return node.asInt();
        } else if (node.isLong()) {
            return node.asLong();
        } else if (node.isDouble()) {
            return node.asDouble();
        } else if(node.isTextual()) {
            return node.asText();
        } else if (node.isNull()){
            return null;
        }
        else {
            throw new IOException("Json node exception type :  " + node.numberType());
        }
    }
}

例如上面的字符串我们将其解析为 常见的Map结构:

Map map = JsonUtil.json2Bean(str, Map.class);
System.out.println(map.get("address"));
List<Map<String, String>> list = (List<Map<String, String>>)map.get("address");
System.out.println(list.get(1).get("addressId"));

当然我们也可以通过 writeValue的方式将普通类型转换成 String 打印出来:

public static String object2Str(Object object, boolean prettyPrint) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();

    // 美化方式打印
    if(prettyPrint){
        ObjectWriter write = objectMapper.writerWithDefaultPrettyPrinter();
        return write.writeValueAsString(object);
    }
    // 单行打印
    return objectMapper.writeValueAsString(object);
}

String 与 Bean 的相互转化

  • 转换工具方法为:
public static <T> T str2Bean(String str, Class<T> beanClass) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    return mapper.readValue(str, beanClass);
}

要将 string -> bean , 对于每一个 jsonStr, 我们都需要创建一个与之对应的 bean class.

例如 :

{
  "name" : "test",
  "value" : 20
}

对应的 Bean 为 :

public class Persion {
    private String name;
    private int value;

    public void setName(String name) {
        this.name = name;
    }

    public void setValue(int value) {
        this.value = value;
    }
    
}

我们通过 Persion persion = JsonUtil.str2Bean(str, Persion.class); 这时候会通过 setter 方法将json内容注入到 Persion类的对应字段中。

如果 json 中的 key 与 Bean 中的字段名称不对应,我们可以通过 @JsonProperty 注解为其声明对应关系。
@JsonProperty 注解可以用于 getter,setter,或者构造函数参数中。
例如 :

public class Persion {
    private String name;
    private int value;

    @JsonProperty("v-name")
    public void setName(String name) {
        this.name = name;
    }

    @JsonProperty("v-value")
    public void setValue(int value) {
        this.value = value;
    }
}
  • 排除未知的字段@JsonIgnoreProperties(ignoreUnknown = true)

有时候json中出了具有bean声明的字段以外,有时候还会有其他信息,但这些信息并不是我们所关系的,我们这时候就需要舍弃这部分内容, 我们可以在类开始处使用 @JsonIgnoreProperties(ignoreUnknown = true) 注解,忽略掉不匹配或者转换失败的字段。

@JsonIgnoreProperties(ignoreUnknown = true)
public class Persion {
    private String name;
    private int value;

    @JsonProperty("v-name")
    public void setName(String name) {
        this.name = name;
    }

    @JsonProperty("v-value")
    public void setValue(int value) {
        this.value = value;
    }
}
  • 自定义序列号与反序列化方式:

有时候 json 与 bean 并不是简单的对应关系, 反序列化过程往往存在困难, 例如对于开始处这个 json 内容 :

{
  "name" : "test",
  "value" : 20,
  "address" : [
    {
     "addressId" : "0001" 
    },
    {
    "addressId" : "0002"
    }
  ]
}

我们的对应 bean 为:

public class Persion {
    private String name;
    private int value;
    private List<Address> address;
}
public class Address {
    private String addressId;

    public void setAddressId(String addressId) {
        this.addressId = addressId;
    }
}

正常情况下, 对于 address 字段会映射为 List<Map> 类型, 但是此处我们需要让他自动转换为 List<Address>
这时候就需要我们自定义反序列化过程:

反序列化类需要继承 JsonDeserializer<T> 泛型对应反序列化的返回类型 :

反序列化工具类

public class AddressDeserde extends JsonDeserializer<List> {
    public List deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode nodes = mapper.readTree(jsonParser);

        List<Address> addresses = new LinkedList<Address>();

        for(JsonNode node :  Lists.newArrayList(nodes.elements())){
            System.out.println(node.toString());
            addresses.add(JsonUtil.str2Bean(node.toString(), Address.class));
        }
        return addresses;
    }
}

然后便可以再 bean 中字段指名反序列化工具:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Persion {
    private String name;
    private int value;
    private List<Address> address;

    @JsonProperty("v-name")
    public void setName(String name) {
        this.name = name;
    }

    @JsonProperty("v-value")
    public void setValue(int value) {
        this.value = value;
    }


    @JsonProperty("address")
    @JsonDeserialize(using = bean.AddressDeserde.class)
    public void setAddress(List<Address> address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Persion{" +
                "name='" + name + '\'' +
                ", value=" + value +
                ", addresses=" + address +
                '}';
    }
}

这时候便可以用了。

同样的,我们要自定义序列化方式需要继承 JsonSerializer<T> 抽象方法

例如对于 address 的字段,的序列化过程:

public class AddressSerde extends JsonSerializer<List<Address>> {
    @Override
    public void serialize(List<Address> addresses, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {

        jsonGenerator.writeStartArray();
        for(Address address : addresses){
            jsonGenerator.writeObject(address);
        }
        jsonGenerator.writeEndArray();


    }
}

我们可以在 getter 方法中加入序列化方式:

@JsonProperty("v-address")
@JsonSerialize(using = bean.AddressSerde.class)
public List<Address> getAddress() {
    return address;
}

然后便能够成功的解析出 List<Address>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,559评论 18 139
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong阅读 22,261评论 1 92
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,706评论 6 342
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,689评论 2 22
  • 感恩陈斯载我去工地见客户,还请吃饭、下午茶哈哈,还学到很多东西 感恩东莞的秋天还是夏天~短袖好舒服 感恩朋友请吃玉...
    爱眉小札夏大宝阅读 160评论 0 0