首页 » Web前端 » phpjsonmapper技巧_二十不惑ObjectMapper运用也不再迷惑

phpjsonmapper技巧_二十不惑ObjectMapper运用也不再迷惑

访客 2024-11-19 0

扫一扫用手机浏览

文章目录 [+]

原文链接:https://mp.weixin.qq.com/s/VYy1QVeFLRkciymFHueb5w

phpjsonmapper技巧_二十不惑ObjectMapper运用也不再迷惑

✍序言

从本文起,终于要和Jackson的“高等”部分打交道了,也便是数据绑定jackson-databind模块。
通过打仗它的高等API,你会持续的创造,前面花那么多篇幅讲的core核心部分是代价连城的。
毕竟村落上春树也见告过我们:「人生没有无用的经历」嘛。

phpjsonmapper技巧_二十不惑ObjectMapper运用也不再迷惑
(图片来自网络侵删)

jackson-databind包含用于Jackson数据处理器的通用 「数据绑定功能」和「树模型」。
它构建在Streaming API之上,并利用Jackson表明进行配置。
它便是Jackson供应的高层API,是开拓者利用得最多的办法,因此主要程度可见一斑。

虽然Jackson最初的用例是JSON数据绑定,但现在它也可以用于其它数据格式,只要存在解析器和天生器实现即可。
但须要把稳的是:类的命名在很多地方仍旧利用了“JSON”这个词(比如JsonGenerator),只管它与JSON格式没有实际的硬依赖关系。

小贴士:底层流式API利用的I/O进行输入输出,因此理论上是支持任何格式的

版本约定Jackson版本:2.11.0Spring Framework版本:5.2.6.RELEASESpring Boot版本:2.3.0.RELEASE

从「本文开始」,新增导包:

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency>

Tips:jackson-databind模块它强依赖于jackson-core和jackson-annotations,只须要导入此包,其余两个它自动会帮带进来。

这里须要解释几句:我们知道core包中还有个jackson-annotations,难道不讲了吗?实在不是,是由于单独讲jackson-annotations并无意义,毕竟表明还得靠数据绑定模块来解析,以是先搞定这个后再杀回去。

✍正文

据我理解,很多小伙伴对Jackson的理解起源于ObjectMapper,止于ObjectMapper。
那么,作为打仗它的第一篇文章咱们就轻松点,以运用为主来整体的认识它。

功能先容

ObjectMapper是jackson-databind模块最为主要的一个类,它完成了coder对数据绑定的「险些所有功能」。
它是面向用户的高层API,底层依赖于Streaming API来实现读/写。
ObjectMapper紧张供应的功能点如下:

它供应读取和写入JSON的功能(最主要的功能)普通POJO的序列化/反序列化JSON树模型的读/写它可以被「高度定制」,以利用不同风格的JSON内容利用Feature进行定制利用可插拔com.fasterxml.jackson.databind.Module模块来扩展/丰富功能它还支持「更高等」的工具观点:比如多态泛型、工具标识它还充当了更为高等(更强大)的API:ObjectReader和ObjectWriter的「工厂」ObjectReader和ObjectWriter底层亦是依赖于Streaming API实现读写

只管绝大部分的读/写API都通过ObjectMapper暴露出去了,但有些功能函数还是只放在了ObjectReader/ObjectWriter里,比如对付读/写 「长序列」 的能力你只能通过ObjectReader#readValues(InputStream) / ObjectWriter#writeValues(OutputStream)去处理,这是设计者故意为之,毕竟这种case很少很少,没必要和常用的凑合在一起嘛。

数据绑定

数据绑定分为大略数据绑定和完备数据绑定:

「大略数据绑定」:比如绑定int类型、List、Map等…

@Testpublicvoidtest1()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();//绑定大略类型和Map类型Integerage=objectMapper.readValue("1",int.class);Mapmap=objectMapper.readValue("{\"name\":\"YourBatman\"}",Map.class);System.out.println(age);System.out.println(map);}

运行程序,输出:

1{name=YourBatman}「完备数据绑定」:绑定到任意的Java Bean工具…

准备一个POJO:

@Data@NoArgsConstructor@AllArgsConstructorpublicclassPerson{privateStringname;privateIntegerage;}

绑天命据到POJO:

@Testpublicvoidtest2()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();Personperson=objectMapper.readValue("{\"name\":\"YourBatman\",\"age\":18}",Person.class);System.out.println(person);}

运行程序,输出:

Person(name=YourBatman,age=18)ObjectMapper的利用

在运用及开拓中,ObjectMapper绝对是最常利用的,也是你利用Jackson的入口,本文就列列它的那些利用场景。

小贴士:树模型会单独成文先容,表示出它的主要性

写(序列化)

供应writeValue()系列方法用于写数据(可写任何类型),也便是我们常说的「序列化」。

writeValue(File resultFile, Object value):写到目标文件里writeValue(OutputStream out, Object value):写到输出流「String writeValueAsString(Object value):写成字符串形式,此方法最为常用」writeValueAsBytes(Object value):写成字节数组byte[]

@Testpublicvoidtest3()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();System.out.println("----------写大略类型----------");System.out.println(objectMapper.writeValueAsString(18));System.out.println(objectMapper.writeValueAsString("YourBatman"));System.out.println("----------写凑集类型----------");System.out.println(objectMapper.writeValueAsString(Arrays.asList(1,2,3)));System.out.println(objectMapper.writeValueAsString(newHashMap<String,String>(){{put("zhName","A哥");put("enName","YourBatman");}}));System.out.println("----------写POJO----------");System.out.println(objectMapper.writeValueAsString(newPerson("A哥",18)));}

运行程序,输出:

----------写大略类型----------18"YourBatman"----------写凑集类型----------[1,2,3]{"zhName":"A哥","enName":"YourBatman"}----------写POJO----------{"name":"A哥","age":18}读(反序列化)

供应readValue()系列方法用于读数据(一样平常读字符串类型),也便是我们常说的「反序列化」。

readValue(String content, Class<T> valueType):读为指定class类型的工具,此方法最常用readValue(String content, TypeReference<T> valueTypeRef):T表示泛型类型,如List<T>这种类型,一样平常用于凑集/Map的反序列化readValue(String content, JavaType valueType):Jackson内置的JavaType类型,后再详解(利用并不多)

@Testpublicvoidtest4()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();System.out.println("----------读大略类型----------");System.out.println(objectMapper.readValue("18",Integer.class));//抛错:JsonParseException 单独的一个串,解析会抛错//System.out.println(objectMapper.readValue("YourBatman",String.class));System.out.println("----------读凑集类型----------");System.out.println(objectMapper.readValue("[1,2,3]",List.class));System.out.println(objectMapper.readValue("{\"zhName\":\"A哥\",\"enName\":\"YourBatman\"}",Map.class));System.out.println("----------读POJO----------");System.out.println(objectMapper.readValue("{\"name\":\"A哥\",\"age\":18}",Person.class));}

运行程序,输出:

----------读大略类型----------18----------读凑集类型----------[1,2,3]{zhName=A哥,enName=YourBatman}----------读POJO----------Person(name=A哥,age=18)

不同于序列化,可以把“所有”写成为一个字符串。
反序列化场景有它分外的地方,比如例子中所示:不能反序列化一个“纯挚的”字符串。

泛型擦除问题

从例举出来的三个read读方法中,就该当以为事情还没完,比如这个带泛型的case:

@Testpublicvoidtest5()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();System.out.println("----------读凑集类型----------");List<Long>list=objectMapper.readValue("[1,2,3]",List.class);Longid=list.get(0);System.out.println(id);}

运行程序,抛错:

----------读凑集类型----------java.lang.ClassCastException:java.lang.Integercannotbecasttojava.lang.Longatcn.yourbatman.jackson.core.ObjectMapperDemo.test5(ObjectMapperDemo.java:100)...

非常栈里指出:Long id = list.get(0);这一句涌现了「类型转换非常」,这便是问题缘故原由所在:泛型擦除,参考图示如下(明明泛型类型是Long,但实际装的是Integer类型):

对这种问题,你可能会“动脑筋”思考:写成[1L,2L,3L]这样行弗成。
思想很生动,奈何现实依旧残酷,运行抛错:

com.fasterxml.jackson.core.JsonParseException:Unexpectedcharacter('L'(code76)):wasexpectingcommatoseparateArrayentriesat[Source:(String)"[1L,2L,3L]";line:1,column:4]...

这是范例的泛型擦除问题。
该问题只可能涌如今读(反序列化)上,不能涌如今写上。
那么这种问题怎么破?

「在办理此问题之前,我们得先对Java中的泛型擦除有所理解,至少知道如下两点结论:」

Java 在编译时会在字节码里指令集之外的地方保留「部分」泛型信息泛型接口、类、方法定义上的所有泛型、成员变量声明处的泛型「都会」被保留类型信息,「其它地方」的泛型信息都会被擦除

此问题在开拓过程中非常高频,有了此理论作为支撑,A哥供应两种可以办理本问题的方案供以参考:

方案一:利用成员变量保留泛型

理论依据:成员变量的泛型类型不会被擦除

@Testpublicvoidtest6()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();System.out.println("----------读凑集类型----------");Datadata=objectMapper.readValue("{\"ids\":[1,2,3]}",Data.class);Longid=data.getIds().get(0);System.out.println(id);}@lombok.DataprivatestaticclassData{privateList<Long>ids;}

运行程序,统统正常:

----------读凑集类型----------1方案二:利用官方推举的TypeReference<T>

官方早早就为我们考虑好了这类泛型擦除的问题,以是它供应了TypeReference<T>方便我们把泛型类型保留下来,利用起来是非常的方便的:

@Testpublicvoidtest7()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();System.out.println("----------读凑集类型----------");List<Long>ids=objectMapper.readValue("[1,2,3]",newTypeReference<List<Long>>(){});Longid=ids.get(0);System.out.println(id);}

运行程序,统统正常:

----------读凑集类型----------1

本方案的理论依据是:泛型接口/类上的泛型类型不会被擦除。

对付泛型擦除情形,办理思路是「hold住」泛型类型,这样反序列化的时候才不会抓瞎。
但凡只要一抓瞎,Jackson就木有办法只能采取「通用/默认类型」去装载喽。

加餐

自2.10版本起,给ObjectMapper供应了一个子类:JsonMapper,使得语义更加明确,专门用于处理JSON格式。

严格意义上讲,ObjectMapper不局限于处理JSON格式,比如后面会讲到的它的其余一个子类YAMLMapper用于对Yaml格式的支持(需额外导包,后面见~)

其余,由于构建一个ObjectMapper实例属于高频动作,因此Jackson也顺应潮流的供应了MapperBuilder构建器(2.10版本起)。
我们可以通过此构建起很随意马虎的得到一个ObjectMapper(以JsonMapper为例)实例来利用:

@Testpublicvoidtest8()throwsJsonProcessingException{JsonMapperjsonMapper=JsonMapper.builder().configure(JsonReadFeature.ALLOW_SINGLE_QUOTES,true).build();Personperson=jsonMapper.readValue("{'name':'YourBatman','age':18}",Person.class);System.out.println(person);}

运行程序,正常输出:

Person(name=YourBatman,age=18)✍总结

本文内容很轻松,讲述了ObjectMapper的日常利用,利用它进行读/写,完成日常功能。

对付写来说比较大略,一个writeValueAsString(obj)方法走天下;但对付读来说,除了利用readValue(String content, Class<T> valueType)自动完成数据绑定外,须要特殊把稳泛型擦除问题:「若反序列化成为一个凑集类型(Collection or Map),泛型会被擦除」,此时你该当利用readValue(String content, TypeReference<T> valueTypeRef)方法代替。

小贴士:若你在工程中碰着objectMapper.readValue(xxx, List.class)这种代码,那肯定是有安全隐患的(但不一定报错)

相关文章

网站SEO优化关键词布局的艺术与步骤

网站SEO优化已成为企业获取流量、提升品牌知名度的重要手段。关键词布局作为SEO优化的核心环节,其重要性不言而喻。本文将深入探讨关...

Web前端 2025-04-09 阅读1 评论0

网站编辑与SEO协同发展的密不可分关系

网站编辑和SEO(搜索引擎优化)这两个职业逐渐成为互联网行业的热门话题。许多人认为,网站编辑和SEO是两个独立的领域,但实际上,它...

Web前端 2025-04-09 阅读1 评论0