`

Json常见用法

    博客分类:
  • json
阅读更多

JACKSON处理JSON的一些常见使用

接下来就介绍一些处理json时常见的使用场景,文中的例子都是在1.9版本下运行的。
Jackson的json库提供了3种API:

  • Streaming API : 性能最好
  • Tree Model : 最灵活
  • Data Binding : 最方便

其中最常用到的就是Data Binding了,基本的用法如下

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(foo);
Foo foo = mapper.readValue(json, Foo.class);

ObjectMapper是线程安全的,应该尽量的重用。
需要注意的是,Jackson是基于JavaBean来串行化属性的,如果属性没有GETTER方法,默认是不会输出该属性的。

但是在串行化的时候,经常会有特殊的需求来对输出的结果进行自定义。
比如不输出某几个属性,或者自定义属性的名字,等等。
Jackson提供了非常多的方法来满足我们的自定义需求。

假设有这么一个对象:

class User {
        private long id;
        private String name;
        private String avator240;
        private String avator160;
        private String address;
        public long getId() {
            return id;
        }
        public String getName() {
            return name;
        }
        public String getAvator240() {
            return avator240;
        }
        public String getAvator160() {
            return avator160;
        }
        public String getAddress() {
            return address;
        }
    }

如果不想输出id,最简单的方法,就是给该属性加上注解JsonIgnore:

@JsonIgnore
private long id;

或者

@JsonIgnore
public long getId() {
    return id;
}

因为JsonIgnoretarget可以是CONSTRUCTOR, FIELD, METHOD

如果不想输出多个属性,比如idaddressavator160,除了在每个属性上添加JsonIgnore,也可以直接在类上添加注解JsonIgnoreProperties:

@JsonIgnoreProperties({"id","avator160","address"})
class User {

这里的User类只有5个属性,使用annotation控制忽略哪些属性还是绰绰有余的。
加入有一个类有上百个属性,如果只想输出其中的10来个属性,使用JsonIgnore就显得太繁琐了。
此时就可以使用JSON View或MixIn Annotation了。

先来看一下JSON View,和数据库的view一样,可以为一个对象创建view,输出时只会输出view中定义的那些属性。 特别的,一个对象可以定义任意多个view,同时view也是可以继承的。
先来看看如何使用view来过滤idaddressavator160

public class JsonViewDemo {
    private static class User {
        private long id;
        @JsonView({FilterView.Output.class})
        private String name;
        @JsonView({FilterView.Output.class})
        private String avator240;
        private String avator160;
        private String address;
        public long getId() {
            return id;
        }
        public String getName() {
            return name;
        }
        public String getAvator240() {
            return avator240;
        }
        public String getAvator160() {
            return avator160;
        }
        public String getAddress() {
            return address;
        }
    }

    private static class FilterView {
        static class Output {}
    }

    public static void main(String[] args) throws Exception {
        User user = new User();
        user.id = 1000L;
        user.name = "test name";
        user.avator240 = "240.jpg";
        user.avator160 = "160.jpg";
        user.address = "some address";

        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
        System.out.println(mapper.writerWithView(FilterView.Output.class).writeValueAsString(user));
    }
}

首先需要定义一个需要输出的属性的View:FilterView.Output,然后在需要输出属性上声明该View,之后使用writerWithView(FilterView.Output.class)来串行化就可以了。
需要注意的是,在这里需要把DEFAULT_VIEW_INCLUSION设置为false,因为默认是会输出没有JsonView注解的属性的。

其实View的作用远不止如此,再来看一个更实用的例子: 假设现有个API接口,需要针对不同的客户端(ios,android)输出不同的属性,通过创建多个View就能轻松完成。

public class JsonApiViewDemo {

    private static class User {
        private long id;

        @JsonView({ApiView.Default.class})
        private String name;

        @JsonView({ApiView.Ios.class})
        private String avator240;

        @JsonView({ApiView.Android.class})
        private String avator160;

        private String address;

        public long getId() {
            return id;
        }
        public String getName() {
            return name;
        }
        public String getAddress() {
            return address;
        }
        public String getAvator240() {
            return avator240;
        }
        public String getAvator160() {
            return avator160;
        }

    }

    private static class ApiView {
        static class Default {}
        static class Ios extends Default {}
        static class Android extends Default {}
    }

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);

        User user = new User();
        user.id = 10000L;
        user.name = "test name";
        user.avator240 = "240.jpg";
        user.avator160 = "160.jpg";
        user.address = "some address";

        String apiViewJson = mapper.writerWithView(ApiView.Default.class).writeValueAsString(user);
        String iosViewJson = mapper.writerWithView(ApiView.Ios.class).writeValueAsString(user);
        String androidViewJson = mapper.writerWithView(ApiView.Android.class).writeValueAsString(user);

        System.out.println(apiViewJson);
        System.out.println(iosViewJson);
        System.out.println(androidViewJson);

    }

}

使用ApiView.Ios只会输出nameavator240
使用ApiView.Android只会输出nameavator160

但是,以上的所有方法都有一个缺点,那就是需要修改源代码,它们都需要在要输出的类上加上annotation。
假设没有那些要串行化的类的源代码,甚至那些类都不符合JavaBean规范,又该怎么办呢? 此时就可以使用MixIn Annotation了,其实和View差不多,也相当于是为要串行化的对象定义了一个View。

public class JsonMixInDemo {
    static class User {
        private long id;
        private String name;
        private String avator240;
        private String avator160;
        private String address;

        public long getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        public String getAddress() {
            return address;
        }

    }

    abstract class MixIn {
        @JsonIgnore abstract int getAddress();

        @JsonIgnore long id;

        @JsonProperty("custom_name") abstract String getName();

        @JsonProperty("avator") String avator240;
    }


    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        User user = new User();
        user.id = 1234567L;
        user.name = "test name";
        user.avator240 = "240.jpg";
        user.avator160 = "160.jpg";
        user.address = "some address";

        mapper.getSerializationConfig().addMixInAnnotations(User.class, MixIn.class);
        String json = mapper.writeValueAsString(user);
        System.out.println(json);

    }
}

将输出

{"custom_name":"test name","avator":"240.jpg"}

其中关键在于MixIn这个类,MixIn也可以定义成接口。
在这里,既可以过滤属性/方法,也可以定义哪些属性/方法会被输出,顺便还可以自定义输出的属性名。
在串行化前只要配置一下

addMixInAnnotations(User.class, MixIn.class)

就可以在完全不修改该类的情况下自定义输出了。

MixIn Annotation应该能满足几乎所有需要对属性进行自定义的情况了,但是MixIn Annotation的配置是静态的,不能在运行时修改。
结合JSON Filter和Mixin就可以实现动态的过滤属性了

public class JsonFilterDemo {

    private static class User {
        private long id;
        private String name;
        private String avator240;
        private String avator160;
        private String address;

        public String getName() {
            return name;
        }
        public String getAddress() {
            return address;
        }
        public String getAvator240() {
            return avator240;
        }
        public String getAvator160() {
            return avator160;
        }
        public long getId() {
            return id;
        }
    }

    @JsonFilter("userFilter")
    private static interface UserFilterMixIn
    {

    }

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        User user = new User();
        user.id = 1000L;
        user.name = "test name";
        user.avator240 = "240.jpg";
        user.avator160 = "160.jpg";
        user.address = "some address";


        FilterProvider idFilterProvider = new SimpleFilterProvider().addFilter("userFilter", SimpleBeanPropertyFilter.filterOutAllExcept(new String[]{"name", "avator240"}));
        mapper.setFilters(idFilterProvider);
        mapper.getSerializationConfig().addMixInAnnotations(User.class, UserFilterMixIn.class);
        String userFilterJson = mapper.writeValueAsString(user);

        System.out.println(userFilterJson);
    }
}

前面介绍了很多自定义输出属性的方法,如果需要在串行化时修改值,要怎么办呢?
只要实现自己的JsonSerializer就可以了,下面这个例子就会输出id的md5值

public class JsonCustomSerializerDemo {
    static class User {
        @JsonSerialize(using = Md5IdSerializer.class)
        private long id;
        private String name;
        private String address;

        public long getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        public String getAddress() {
            return address;
        }

    }

    public static class Md5IdSerializer extends JsonSerializer
   
   
    
     {

        public void serialize(Long value, JsonGenerator generator, SerializerProvider provider)
                throws IOException, JsonProcessingException {
            generator.writeString(md5(value));
        }

        private String md5(Long value) {
            return value + "-md5-mock";
        }

    }

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        User user = new User();
        user.id = 1234567L;
        user.name = "test name";
        user.address = "some address";

        String json = mapper.writeValueAsString(user);
        System.out.println(json);

    }
}
   
   

接下来看一下反串行化,现在很多网站都开放了api接口,支持json格式的返回。
比如在调用了某个api后,需要解析返回的json数据获取信息,这种情况下为json创建一个对应的类是很不方便的。
此时使用Tree Model来解析json就比较方便了,下面这段代码就是用来解析人人网的用户信息的

JsonNode root = mapper.readTree(rerenjson);
JsonNode user = root.get("user");
String id = user.get("id").asText();
String name = user.get("name").asText();
JsonNode avators = user.get("avatar");
if (avators.isArray()) {
    for (Iterator
   
   
    
     it = avators.getElements(); it.hasNext(); ){
        JsonNode avator = it.next();
        if ("tiny".equals(avator.get("type").asText())) {
            String ava = avator.get("url").asText();
            break;
        }
    }
}

   
   

最后列一些使用Jackson的最佳实践:

  • 重用重量级对象: ObjectMapper, JsonFactory
  • 串行化性能(从高到低): OutputStream > Writer > writeValueAsString
  • 反串行化性能(从高到低): byte[] > InputStream > Reader
  • 用更轻量ObjectReader/ObjectWriter替代ObjectMapper
  • 及时关闭JsonParser, JsonGenerator

上面这些tips都摘自https://github.com/FasterXML/jackson-docs/wiki/Presentation:-Jackson-Performance,上面还有更多tips。

分享到:
评论

相关推荐

    python的JSON用法——dumps的各种参数用法(详细)

    目录一、JSON是什么1.json的数据类型和python数据类型的区别2.json库的一些方法二、...JSON (JavaScript Object Notation)最初是用 JavaScript 对象表示法编写的文本,但随后成为了一种常见格式,被包括Python在内的

    loadjson:加载json安全&&模拟api

    加载常见的json var loadjson = require ( 'loadjson' ) var config = loadjson ( 'package.json' ) if ( ! config . error ) { console . log ( config ) ; console . log ( config . name ) ; } api成功 var ...

    json格式的javascript对象用法分析

    主要介绍了json格式的javascript对象用法,结合实例形式总结分析了javascript针对json格式数据操作的常见技巧,需要的朋友可以参考下

    json-uri:小型且与浏览器兼容的URL友好型JSON转换器

    json-uri json-uri是一个与uri兼容的简单json转换器,具有明显的优势: 与浏览器兼容,无依赖... uri兼容json翻译的最常见用法是通过网络url传递JSON对象,并且 由于浏览器和服务器的限制,通常将长度超过2kB或4kB的u

    circle-json:JSON不处理循环引用。 现在可以了

    一个常见问题的可行解决方案 一个用法示例: var object = { } ; object . arr = [ object , object ] ; object . arr . push ( object . arr ) ; object . obj = object ; var serialized = CircularJSON . ...

    GoldenFleece:Argo JSON 解析器常见类型的扩展集合

    JSON 解析器的常见类型扩展的集合。 在希腊神话中,阿尔戈是杰森和阿尔戈英雄从伊奥尔科斯出发去取回金羊毛的船。 用法 给定以下 JSON,GoldenFleece 允许我们使用 Argo/Runes 直接从profile_picture解析NSURL和从...

    FifteenBelow.Json:Newtonsoft.Json 的 F# JSON 转换器

    FifteenBelow.Json 为 Newtonsoft.Json 库提供了一组JsonConverter类型,专注于提供常见 F# 类型的惯用序列化。 虽然 Newtonsoft.Json 正在推进对 F# 的原生支持,但我们认为这些转换器发出的 JSON 结构稍微更人性化...

    broccoli-static-site-json:将静态Markdown文件构建到JSON中

    最常见的用法是在这样的content文件夹上调用StaticSiteJson : const contentJsonTree = new StaticSiteJson('content') 。 有关默认行为的重要说明: 文件夹的名称将是JSON:API文档的默认type 。 该类型将自动...

    JSON-RPC_PHP_full.zip

    jsonrpc 基本认为是一种xmlrpc的替代方案。在php中应用jsonrpc,是常见的一种用法。此方案中,给出了php权威的调用方法示例。-Using jsonrpc in php.

    angular-json-tree:用于在可扩展树结构中显示JSON对象的Angular指令

    创建此插件是为了解决一个常见的开发问题:如何轻松可视化JSON / JS对象树。 从开发的角度来看,当查询API端点并希望轻松地可视化响应对象时(或者从设计的角度来看,当寻找一种显示复杂对象的简便方法时),此插件...

    python的json中方法及jsonpath模块用法分析

    主要介绍了python的json中方法及jsonpath模块用法,结合实例形式分析了Python json相关模块与常见方法使用技巧,需要的朋友可以参考下

    JSON 使用

    JSON 最常见的用法之一,是从 web 服务器上读取 JSON 数据(作为文件或作为 HttpRequest),将 JSON 数据转换为 JavaScript 对象,然后在网页中使用该数据。 为了更简单地为您讲解,我们使用字符串作为输入进行演示...

    syntactor:JSON编辑器,用于编辑数据而不是语法

    与其他编辑器相比,击键次数或多或少的常见代码转换 目前,它只是一个JSON编辑器。 用法 在您的命令行中: yarn add syntactor # or npm install syntactor --save 在您的代码中: const Syntactor = require ( '...

    Python常见读写文件操作实例总结【文本、json、csv、pdf等】

    读写文件是最常见的IO操作,python内置了读写文件的函数,用法和c是兼容的. 读写文件前,我们必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以读写文件就是...

    go-map-schema:简单的JSON类型检查-Golang开发

    go-map-schema是一个很小的库,对于将地图(通常是JSON中的地图)与结构进行比较...用例最常见的用法是用于接受来自客户端的JSON的API。 在满足请求之前,我们需要知道JSON是否符合我们的期望。 通过在我们之前验证JSON

    docker-aws:用于在AWS容器服务上运行容器的Dockerfiles和Dockeraws.run.json文件

    使用Elastic Beanstalk将Docker VM部署到AWS 公司代理未配置为这些配置的一部分-因为将这些VM推送到docker.io以供AWS...常见AWS命令 一旦连接到EC2实例,这些命令就可用。 检查EC2容器的状态 curl http://localhost:5

    php使用redis的几种常见操作方式和用法示例

    本文实例讲述了php使用redis的几种常见操作方式和用法。分享给大家供大家参考,具体如下: 一、简单的字符串缓存 比如针对一些sql查询较慢,更新不频繁的数据进行缓存。 <?php $redis = new Redis(); $redis->...

    MaterialDesign-Meta:物料设计图标的区域meta.json

    物料设计图标-meta.json 物料设计图标的区域meta.json用法对于某些项目,在只需要meta.json文件的情况下,可能不希望使用整个@mdi/svg devDependency。 这种依赖关系最常见的用途是搜索图标,因此下面的示例将对此...

    web-data-extractor:使用jQuery Selector,XPath或JsonPath从常见的Web格式(例如HTML,XML和JSON)提取和解析结构化数据

    网络数据提取器 使用Jquery Selector,XPath或JsonPath从常见的Web格式(例如HTML,XML和JSON)提取和解析结构化数据。 实施: jQuery选择器和 XPath- JsonPath- 用法要使用Maven添加对Web-Data-Extractor的依赖关系...

Global site tag (gtag.js) - Google Analytics