热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JackSon详解

一、简介Jackson是当前用的比较广泛的,用来序列化和反序列化json的Java的开源框架。Jackson社区相对比较活跃,更新速度也比较快

一、简介

Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。Jackson 社区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson 是最流行的 json 解析器之一 。 Spring MVC 的默认 json 解析器便是 Jackson。 Jackson 优点很多。 Jackson 所依赖的 jar 包较少 ,简单易用。与其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比较快;Jackson 运行时占用内存比较低,性能比较好;Jackson 有灵活的 API,可以很容易进行扩展和定制。

Jackson 的 1.x 版本的包名是 org.codehaus.jackson ,当升级到 2.x 版本时,包名变为 com.fasterxml.jackson。

Jackson 的核心模块由三部分组成。


  • jackson-core,核心包,提供基于"流模式"解析的相关 API,它包括 JsonPaser 和 JsonGenerator。 Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。
  • jackson-annotations,注解包,提供标准注解功能;
  • jackson-databind ,数据绑定包, 提供基于"对象绑定" 解析的相关 API ( ObjectMapper ) 和"树模型" 解析的相关 API (JsonNode);基于"对象绑定" 解析的 API 和"树模型"解析的 API 依赖基于"流模式"解析的 API。

源码地址:FasterXML/jackson


二、依赖

使用Maven构建项目,需要添加依赖:

com.fasterxml.jackson.corejackson-core2.9.6
com.fasterxml.jackson.corejackson-annotations2.9.6
com.fasterxml.jackson.corejackson-databind2.9.6

当然了,jackson-databind 依赖 jackson-core 和 jackson-annotations,所以可以只显示地添加jackson-databind依赖,jackson-core 和 jackson-annotations 也随之添加到 Java 项目工程中。

com.fasterxml.jackson.corejackson-databind2.9.6

下面是Jackson的用法。


三、 ObjectMapper

Jackson 最常用的 API 就是基于"对象绑定" 的 ObjectMapper:


  • ObjectMapper可以从字符串,流或文件中解析JSON,并创建表示已解析的JSON的Java对象。 将JSON解析为Java对象也称为从JSON反序列化Java对象。

  • ObjectMapper也可以从Java对象创建JSON。 从Java对象生成JSON也称为将Java对象序列化为JSON。

  • Object映射器可以将JSON解析为自定义的类的对象,也可以解析置JSON树模型的对象。

之所以称为ObjectMapper是因为它将JSON映射到Java对象(反序列化),或者将Java对象映射到JSON(序列化)。


一)、从JSON中获取Java对象


1、简单示例

一个简单的例子:

Car类:

public class Car {private String brand = null;private int doors = 0;public String getBrand() { return this.brand; }public void setBrand(String brand){ this.brand = brand;}public int getDoors() { return this.doors; }public void setDoors (int doors) { this.doors = doors; }
}

将Json转换为Car类对象:

ObjectMapper objectMapper = new ObjectMapper();String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";try {Car car = objectMapper.readValue(carJson, Car.class);System.out.println("car brand = " + car.getBrand());System.out.println("car doors = " + car.getDoors());} catch (IOException e) {e.printStackTrace();}


2、 ObjectMapper如何匹配JSON对象的字段和Java对象的属性

默认情况下,Jackson通过将JSON字段的名称与Java对象中的getter和setter方法进行匹配,将JSON对象的字段映射到Java对象中的属性。 Jackson删除了getter和setter方法名称的“ get”和“ set”部分,并将其余名称的第一个字符转换为小写。

例如,名为brand的JSON字段与名为getBrand()和setBrand()的Java getter和setter方法匹配。 名为engineNumber的JSON字段将与名为getEngineNumber()和setEngineNumber()的getter和setter匹配。

如果需要以其他方式将JSON对象字段与Java对象字段匹配,则需要使用自定义序列化器和反序列化器,或者使用一些Jackson注解。


3、JSON字符串-->Java对象

从JSON字符串读取Java对象非常容易。 上面已经有了一个示例——JSON字符串作为第一个参数传递给ObjectMapper的readValue()方法。 这是另一个简单的示例:

ObjectMapper objectMapper = new ObjectMapper();String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";Car car = objectMapper.readValue(carJson, Car.class);


3、JSON 字符输入流-->Java对象

还可以从通过Reader实例加载的JSON中读取对象。示例如下:

ObjectMapper objectMapper = new ObjectMapper();String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 4 }";
Reader reader = new StringReader(carJson);Car car = objectMapper.readValue(reader, Car.class);


4、JSON文件-->Java对象

从文件读取JSON当然可以通过FileReader(而不是StringReader)来完成,也可以通过File对象来完成。 这是从文件读取JSON的示例:

ObjectMapper objectMapper = new ObjectMapper();File file = new File("data/car.json");Car car = objectMapper.readValue(file, Car.class);


5、JSON via URL--->Java对象

可以通过URL(java.net.URL)从JSON读取对象,如下所示:

ObjectMapper objectMapper = new ObjectMapper();URL url = new URL("file:data/car.json");Car car = objectMapper.readValue(url, Car.class);

示例使用文件URL,也可以使用HTTP URL(类似于http://jenkov.com/some-data.json)。


6、JSON字节输入流-->Java对象

也可以使用ObjectMapper通过InputStream从JSON读取对象。 这是一个从InputStream读取JSON的示例:

ObjectMapper objectMapper = new ObjectMapper();InputStream input = new FileInputStream("data/car.json");Car car = objectMapper.readValue(input, Car.class);


7、JSON二进制数组-->Java对象

Jackson还支持从JSON字节数组读取对象。 这是从JSON字节数组读取对象的示例:

ObjectMapper objectMapper = new ObjectMapper();String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";byte[] bytes = carJson.getBytes("UTF-8");Car car = objectMapper.readValue(bytes, Car.class);


8、JSON数组字符串-->Java对象数组

Jackson ObjectMapper也可以从JSON数组字符串读取对象数组。 这是从JSON数组字符串读取对象数组的示例:

String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";ObjectMapper objectMapper = new ObjectMapper();Car[] cars2 = objectMapper.readValue(jsonArray, Car[].class);

需要将Car数组类作为第二个参数传递给readValue()方法。

读取对象数组还可以与字符串以外的其他JSON源一起使用。 例如,文件,URL,InputStream,Reader等。


9、JSON数组字符串-->List

Jackson ObjectMapper还可以从JSON数组字符串读取对象的Java List。 这是从JSON数组字符串读取对象列表的示例:

String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";ObjectMapper objectMapper = new ObjectMapper();List cars1 = objectMapper.readValue(jsonArray, new TypeReference>(){});


10、JSON字符串-->Map

Jackson ObjectMapper还可以从JSON字符串读取Java Map。 如果事先不知道将要解析的确切JSON结构,这种方法是很有用的。 通常,会将JSON对象读入Java Map。 JSON对象中的每个字段都将成为Java Map中的键,值对。

这是一个使用Jackson ObjectMapper从JSON字符串读取Java Map的示例:

String jsonObject = "{\"brand\":\"ford\", \"doors\":5}";ObjectMapper objectMapper = new ObjectMapper();
Map jsonMap = objectMapper.readValue(jsonObject,new TypeReference>(){});


11、忽略未知的JSON字段

有时候,与要从JSON读取的Java对象相比,JSON中的字段更多。 默认情况下,Jackson在这种情况下会抛出异常,报不知道XYZ字段异常,因为在Java对象中找不到该字段。

但是,有时应该允许JSON中的字段多于相应的Java对象中的字段。 例如,要从REST服务解析JSON,而该REST服务包含的数据远远超出所需的。 在这种情况下,可以使用Jackson配置忽略这些额外的字段。 以下是配置Jackson ObjectMapper忽略未知字段的示例:

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);


12、不允许基本类型为null

如果JSON字符串包含其值设置为null的字段(对于在相应的Java对象中是基本数据类型(int,long,float,double等)的字段),Jackson ObjectMapper默认会处理基本数据类型为null的情况,我们可以可以将Jackson ObjectMapper默认配置为失效,这样基本数据为null就会转换失败。 例如以下Car类:

public class Car {private String brand = null;private int doors = 0;public String getBrand() { return this.brand; }public void setBrand(String brand){ this.brand = brand;}public int getDoors(){ return this.doors; }public void setDoors (int doors) { this.doors = doors; }
}

doors字段是一个int类型,它是Java中的基本数据类型。

现在,假设有一个与Car对象相对应的JSON字符串,如下所示:

{ "brand":"Toyota", "doors":null }

请注意,doors字段值为null。 Java中的基本数据类型不能为null值。 默认情况下,Jackson ObjectMapper会忽略原始字段的空值。 但是,可以将Jackson ObjectMapper配置设置为失败。

ObjectMapper objectMapper = new ObjectMapper();objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);

在FAIL_ON_NULL_FOR_PRIMITIVES配置值设置为true的情况下,尝试将空JSON字段解析为基本类型Java字段时会遇到异常。 这是一个Java Jackson ObjectMapper示例,该示例将失败,因为JSON字段包含原始Java字段的空值:

ObjectMapper objectMapper = new ObjectMapper();objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);String carJson = "{ \"brand\":\"Toyota\", \"doors\":null }";Car car = objectMapper.readValue(carJson, Car.class);

结果:


13、自定义反序列化

有时,可能希望以不同于Jackson ObjectMapper缺省方式的方式将JSON字符串读入Java对象。 可以将自定义反序列化器添加到ObjectMapper,可以按需要执行反序列化。

这是在Jackson的ObjectMapper中注册和使用自定义反序列化器的方式:

String json = "{ \"brand\" : \"Ford\", \"doors\" : 6 }";SimpleModule module =new SimpleModule("CarDeserializer", new Version(3, 1, 8, null, null, null));module.addDeserializer(Car.class, new CarDeserializer(Car.class));ObjectMapper mapper = new ObjectMapper();mapper.registerModule(module);Car car = mapper.readValue(json, Car.class);

自定义反序列化器CarDeserializer类:

public class CarDeserializer extends StdDeserializer {public CarDeserializer(Class vc) {super(vc);}@Overridepublic Car deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException {Car car = new Car();while(!parser.isClosed()){JsonToken jsonToken = parser.nextToken();if(JsonToken.FIELD_NAME.equals(jsonToken)){String fieldName = parser.getCurrentName();System.out.println(fieldName);jsonToken = parser.nextToken();if("brand".equals(fieldName)){car.setBrand(parser.getValueAsString());} else if ("doors".equals(fieldName)){car.setDoors(parser.getValueAsInt());}}}return car;}
}


二)、将对象写入JSON


1、Java对象-->JSON

Jackson ObjectMapper也可以用于从对象生成JSON。 可以使用以下方法之一进行操作:


  • writeValue()
  • writeValueAsString()
  • writeValueAsBytes()

这是一个从Car对象生成JSON的示例,和上面的实例相反:

ObjectMapper objectMapper = new ObjectMapper();Car car = new Car();car.setBrand("BMW");car.setDoors(4);objectMapper.writeValue(new FileOutputStream("data/output-2.json"), car);

此示例首先创建一个ObjectMapper,然后创建一个Car实例,最后调用ObjectMapper的writeValue()方法,该方法将Car对象转换为JSON并将其写入给定的FileOutputStream。

ObjectMapper的writeValueAsString()和writeValueAsBytes()都从一个对象生成JSON,并将生成的JSON作为String或字节数组返回。 示例如下:

ObjectMapper objectMapper = new ObjectMapper();Car car = new Car();car.setBrand("宝马");car.setDoors(4);String json = objectMapper.writeValueAsString(car);System.out.println(json);

运行结果:


2、自定义序列化

有时,想要将Java对象序列化为JSON的方式与使用Jackson的默认方式不同。 例如,可能想要在JSON中使用与Java对象中不同的字段名称,或者希望完全省略某些字段。

Jackson可以在ObjectMapper上设置自定义序列化器。 该序列化器已为某个类注册,然后在每次要求ObjectMapper序列化Car对象时将调用该序列化器。

这是为Car类注册自定义序列化器的示例:

CarSerializer carSerializer = new CarSerializer(Car.class);ObjectMapper objectMapper = new ObjectMapper();SimpleModule module =new SimpleModule("CarSerializer", new Version(2, 1, 3, null, null, null));module.addSerializer(Car.class, carSerializer);objectMapper.registerModule(module);Car car = new Car();car.setBrand("Mercedes");car.setDoors(5);String carJson = objectMapper.writeValueAsString(car);

自定义序列化器CarSerializer类:

public class CarSerializer extends StdSerializer {protected CarSerializer(Class t) {super(t);}public void serialize(Car car, JsonGenerator jsonGenerator,SerializerProvider serializerProvider)throws IOException {jsonGenerator.writeStartObject();jsonGenerator.writeStringField("producer", car.getBrand());jsonGenerator.writeNumberField("doorCount", car.getDoors());jsonGenerator.writeEndObject();}
}

运行结果:

在这里插入图片描述


三)、Jackson 日期转化

默认情况下,Jackson会将java.util.Date对象序列化为其long型的值,该值是自1970年1月1日以来的毫秒数。但是,Jackson还支持将日期格式化为字符串。


1、Date-->long

默认的Jackson日期格式,该格式将Date序列化为自1970年1月1日以来的毫秒数(long类型)。

这是一个包含Date字段的Java类示例:

private String type = null;private Date date = null;public Transaction() {}public Transaction(String type, Date date) {this.type = type;this.date = date;}public String getType() {return type;}public void setType(String type) {this.type = type;}public Date getDate() {return date;}public void setDate(Date date) {this.date = date;}

就像使用其他Java对象进行序列化一样,代码如下:

Transaction transaction = new Transaction("transfer", new Date());ObjectMapper objectMapper = new ObjectMapper();String output = objectMapper.writeValueAsString(transaction);System.out.println(output);

运行结果:


2、Date-->String

日期的long序列化格式不符合人类的时间查看格式。 因此,Jackson也支持文本日期格式。 可以通过在ObjectMapper上设置SimpleDateFormat来指定要使用的确切Jackson日期格式。 这是在Jackson的ObjectMapper上设置SimpleDateFormat的示例:

Transaction transaction = new Transaction("transfer", new Date());ObjectMapper objectMapper = new ObjectMapper();SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");objectMapper.setDateFormat(dateFormat);String output2 = objectMapper.writeValueAsString(transaction);System.out.println(output2);

运行结果:


四)、Jackson JSON 树模型

Jackson具有内置的树模型,可用于表示JSON对象。 如果不知道接收到的JSON的格式,或者由于某种原因而不能(或者只是不想)创建一个类来表示它,那么就要用到Jackson的树模型。 如果需要在使用或转化JSON之前对其进行操作,也需要被用到Jackson树模型。 所有这些情况在数据流场景中都很常见。

Jackson树模型由JsonNode类表示。 您可以使用Jackson ObjectMapper将JSON解析为JsonNode树模型,就像使用您自己的类一样。

以下将展示如何使用Jackson ObjectMapper读写JsonNode实例。


1、Jackson Tree Model简单例子

下面是一个简单的例子:

String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";ObjectMapper objectMapper = new ObjectMapper();try {JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);} catch (IOException e) {e.printStackTrace();}

只需将JsonNode.class作为第二个参数传递给readValue()方法,而不是本教程前面的示例中使用的Car.class,就可以将JSON字符串解析为JsonNode对象而不是Car对象。 。

ObjectMapper类还具有一个特殊的readTree()方法,该方法返回JsonNode。 这是使用ObjectMapper readTree()方法将JSON解析为JsonNode的示例:

String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";ObjectMapper objectMapper = new ObjectMapper();try {JsonNode jsonNode = objectMapper.readTree(carJson);} catch (IOException e) {e.printStackTrace();
}


2、Jackson JsonNode类

通过JsonNode类,可以以非常灵活和动态的方式将JSON作为Java对象导航。这里了解一些如何使用它的基础知识。

将JSON解析为JsonNode(或JsonNode实例树)后,就可以浏览JsonNode树模型。 这是一个JsonNode示例,显示了如何访问JSON字段,数组和嵌套对象:

String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5," +" \"owners\" : [\"John\", \"Jack\", \"Jill\"]," +" \"nestedObject\" : { \"field\" : \"value\" } }";ObjectMapper objectMapper = new ObjectMapper();try {JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);JsonNode brandNode = jsonNode.get("brand");String brand = brandNode.asText();System.out.println("brand = " + brand);JsonNode doorsNode = jsonNode.get("doors");int doors = doorsNode.asInt();System.out.println("doors = " + doors);JsonNode array = jsonNode.get("owners");JsonNode jsonNode = array.get(0);String john = jsonNode.asText();System.out.println("john = " + john);JsonNode child = jsonNode.get("nestedObject");JsonNode childField = child.get("field");String field = childField.asText();System.out.println("field = " + field);} catch (IOException e) {e.printStackTrace();
}

请注意,JSON字符串现在包含一个称为owners的数组字段和一个称为nestedObject的嵌套对象字段。

无论访问的是字段,数组还是嵌套对象,都可以使用JsonNode类的get()方法。 通过将字符串作为参数提供给get()方法,可以访问JsonNode的字段。 如果JsonNode表示数组,则需要将索引传递给get()方法。 索引指定要获取的数组元素。


3、Java对象-->JsonNode

可以使用Jackson ObjectMapper将Java对象转换为JsonNode,而JsonNode是转换后的Java对象的JSON表示形式。 可以通过Jackson ObjectMapper valueToTree()方法将Java对象转换为JsonNode。 这是一个使用ObjectMapper valueToTree()方法将Java对象转换为JsonNode的示例:

ObjectMapper objectMapper = new ObjectMapper();Car car = new Car();
car.brand = "Cadillac";
car.doors = 4;JsonNode carJsonNode = objectMapper.valueToTree(car);


4、JsonNode-->Java对象

可以使用Jackson ObjectMapper treeToValue()方法将JsonNode转换为Java对象。 这类似于使用Jackson Jackson的ObjectMapper将JSON字符串(或其他来源)解析为Java对象。 唯一的区别是,JSON源是JsonNode。 这是一个使用Jackson ObjectMapper treeToValue()方法将JsonNode转换为Java对象的示例:

ObjectMapper objectMapper = new ObjectMapper();String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";JsonNode carJsonNode = objectMapper.readTree(carJson);Car car = objectMapper.treeToValue(carJsonNode);

上面的示例有点“人为”,因为我们首先将JSON字符串转换为JsonNode,然后将JsonNode转换为Car对象。 显然,如果我们有对原始JSON字符串的引用,则最好将其直接转换为Car对象,而无需先将其转换为JsonNode。


四、JsonNode

Jackson JsonNode类com.fasterxml.jackson.databind.JsonNode是Jackson的JSON树形模型(对象图模型)。 Jackson可以将JSON读取到JsonNode实例中,然后将JsonNode写入JSON。 因此,这一节将说明如何将JSON反序列化为JsonNode以及将JsonNode序列化为JSON。 此Jackson JsonNode教程还将说明如何从头开始构建JsonNode对象图,因此以后可以将它们序列化为JSON。


1、JsonNode vs ObjectNode

Jackson JsonNode类是不可变的。 这意味着,实际上不能直接构建JsonNode实例的对象图。 而是创建JsonNode子类ObjectNode的对象图。 作为JsonNode的子类,可以在可以使用JsonNode的任何地方使用ObjectNode。


2、JSON-->JsonNode

要使用Jackson将JSON读取到JsonNode中,首先需要创建一个Jackson ObjectMapper实例。 在ObjectMapper实例上,调用readTree()并将JSON源作为参数传递。 这是将JSON反序列化为JsonNode的示例:

String json = "{ \"f1\" : \"v1\" } ";ObjectMapper objectMapper = new ObjectMapper();JsonNode jsonNode = objectMapper.readTree(json);System.out.println(jsonNode.get("f1").asText());


3、JsonNode-->JSON

要将Jackson的JsonNode写入JSON,还需要一个Jackson ObjectMapper实例。 在ObjectMapper上,调用writeValueAsString()方法或任何适合需要的写入方法。 这是将JsonNode写入JSON的示例:

ObjectMapper objectMapper = new ObjectMapper();JsonNode jsonNode = readJsonIntoJsonNode();String json = objectMapper.writeValueAsString(jsonNode);


4、获取JsonNode字段

JsonNode可以像JSON对象一样具有字段。 假设已将以下JSON解析为JsonNode:

{"field1" : "value1","field2" : 999
}

此JSON对象具有两个名为field1和field2的字段。 如果有一个表示上述JSON对象的Jackson JsonNode,则可以这样获得两个字段:

JsonNode jsonNode = ... //parse above JSON into a JsonNodeJsonNode field1 = jsonNode.get("field1");
JsonNode field2 = jsonNode.get("field2");

请注意,即使两个字段都是String字段,get()方法也始终返回JsonNode来表示该字段。


5、在路径中获取JsonNode字段

Jackson JsonNode有一个称为at()的特殊方法。 at()方法可以从JSON图中以给定JsonNode为根的任何位置访问JSON字段。 假设JSON结构如下所示:

{"identification" : {"name" : "James","ssn: "ABC123552"}
}

如果将此JSON解析为JsonNode,则可以使用at()方法访问名称字段,如下所示:

JsonNode nameNode = jsonNode.at("/identification/name");

注意传递给at()方法的参数:字符串/ identification / name。 这是一个JSON路径表达式。 此路径表达式指定从根JsonNode到您要访问其值的字段的完整路径。 这类似于从文件系统根目录到Unix文件系统中文件的路径。

请注意,JSON路径表达式必须以斜杠字符(/字符)开头。

at()方法返回一个JsonNode,它表示请求的JSON字段。 要获取该字段的实际值,需要调用下一部分介绍的方法之一。 如果没有节点与给定的路径表达式匹配,则将返回null。


6、转换JsonNode字段

Jackson JsonNode类包含一组可以将字段值转换为另一种数据类型的方法。 例如,将String字段值转换为long或相反。 这是将JsonNode字段转换为一些更常见的数据类型的示例:

String f2Str = jsonNode.get("f2").asText();
double f2Dbl = jsonNode.get("f2").asDouble();
int f2Int = jsonNode.get("f2").asInt();
long f2Lng = jsonNode.get("f2").asLong();

使用默认值转换: 如果JsonNode中的字段可以为null,则在尝试转换它时可以提供默认值。 这是使用默认值调用转换方法的示例:

ObjectMapper objectMapper = new ObjectMapper();String json = "{ \"f1\":\"Hello\", \"f2\":null }";JsonNode jsonNode = objectMapper.readTree(json);String f2Value = jsonNode.get("f2").asText("Default");

在示例的JSON字符串中可以看到,声明了f2字段,但将其设置为null。 在这种情况下,调用jsonNode.get(“ f2”)。asText(“ Default”)将返回默认值,在此示例中为字符串Default。

asDouble(),asInt()和asLong()方法还可以采用默认参数值,如果尝试从中获取值的字段为null,则将返回默认参数值。

请注意,如果该字段在JSON中未显式设置为null,但在JSON中丢失,则调用jsonNode.get(“ fieldName”)将返回Java null值,您无法在该Java值上调用asInt() ,asDouble(),asLong()或asText()。 如果尝试这样做,将会导致NullPointerException。 这是说明这种情况的示例:

ObjectMapper objectMapper = new ObjectMapper();String json = "{ \"f1\":\"Hello\" }";JsonNode jsonNode = objectMapper.readTree(json);JsonNode f2FieldNode = jsonNode.get("f2");


7、创建一个ObjectNode

如前所述,JsonNode类是不可变的。 要创建JsonNode对象图,必须能够更改图中的JsonNode实例,例如 设置属性值和子JsonNode实例等。由于是不可变的,因此无法直接使用JsonNode来实现。

而是创建一个ObjectNode实例,该实例是JsonNode的子类。 这是一个通过Jackson ObjectMapper createObjectNode()方法创建ObjectNode的示例:

ObjectMapper objectMapper = new ObjectMapper();ObjectNode objectNode = objectMapper.createObjectNode();


8、Set ObjectNode字段

要在Jackson ObjectNode上设置字段,可以调用其set()方法,并将字段名称String和JsonNode作为参数传递。 这是在Jackson的ObjectNode上设置字段的示例:

ObjectMapper objectMapper = new ObjectMapper();
ObjectNode parentNode = objectMapper.createObjectNode();JsonNode childNode = readJsonIntoJsonNode();parentNode.set("child1", childNode);


9、Put ObjectNode字段

ObjectNode类还具有一组方法,可以直接为字段put(设置)值。 这比尝试将原始值转换为JsonNode并使用set()进行设置要容易得多。 以下是使用put()方法为ObjectNode上的字段设置字符串值的示例:

objectNode.put("field1", "value1");
objectNode.put("field2", 123);
objectNode.put("field3", 999.999);


10、删除字段

ObjectNode类具有一个称为remove()的方法,该方法可用于从ObjectNode中删除字段。 这是一个通过其remove()方法从Jackson ObjectNode删除字段的示例:

objectNode.remove("fieldName");


11、循环JsonNode字段

JsonNode类具有一个名为fieldNames()的方法,该方法返回一个Iterator,可以迭代JsonNode的所有字段名称。 我们可以使用字段名称来获取字段值。 这是一个迭代Jackson JsonNode的所有字段名称和值的示例:

Iterator fieldNames = jsonNode.fieldNames();while(fieldNames.hasNext()) {String fieldName = fieldNames.next();JsonNode field = jsonNode.get(fieldName);
}


五、JsonParser

Jackson JsonParser类是一个底层一些的JSON解析器。 它类似于XML的Java StAX解析器,差别是JsonParser解析JSON而不解析XML。

Jackson JsonParser的运行层级低于Jackson ObjectMapper。 这使得JsonParser比ObjectMapper更快,但使用起来也比较麻烦。


1、创建一个JsonParser

为了创建Jackson JsonParser,首先需要创建一个JsonFactory。 JsonFactory用于创建JsonParser实例。 JsonFactory类包含几个createParser()方法,每个方法都使用不同的JSON源作为参数。

这是创建一个JsonParser来从字符串中解析JSON的示例:

String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(carJson);


2、用JsonParser转化JSON

一旦创建了Jackson JsonParser,就可以使用它来解析JSON。 JsonParser的工作方式是将JSON分解为一系列令牌,可以一个一个地迭代令牌。

这是一个JsonParser示例,它简单地循环遍历所有标记并将它们输出到System.out。 这是一个实际上很少用示例,只是展示了将JSON分解成的令牌,以及如何遍历令牌的基础知识。

tring carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(carJson);while(!parser.isClosed()){JsonToken jsonToken = parser.nextToken();System.out.println("jsonToken = " + jsonToken);
}

只要JsonParser的isClosed()方法返回false,那么JSON源中仍然会有更多的令牌。

可以使用JsonParser的nextToken()获得一个JsonToken。 您可以使用此JsonToken实例检查给定的令牌。 令牌类型由JsonToken类中的一组常量表示。 这些常量是:

START_OBJECT
END_OBJECT
START_ARRAY
END_ARRAY
FIELD_NAME
VALUE_EMBEDDED_OBJECT
VALUE_FALSE
VALUE_TRUE
VALUE_NULL
VALUE_STRING
VALUE_NUMBER_INT
VALUE_NUMBER_FLOAT

可以使用这些常量来找出当前JsonToken是什么类型的令牌。 可以通过这些常量的equals()方法进行操作。 这是一个例子:

String carJson ="{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(carJson);Car car = new Car();
while(!parser.isClosed()){JsonToken jsonToken = parser.nextToken();if(JsonToken.FIELD_NAME.equals(jsonToken)){String fieldName = parser.getCurrentName();System.out.println(fieldName);jsonToken = parser.nextToken();if("brand".equals(fieldName)){car.brand = parser.getValueAsString();} else if ("doors".equals(fieldName)){car.doors = parser.getValueAsInt();}}
}System.out.println("car.brand = " + car.brand);
System.out.println("car.doors = " + car.doors);

如果指向的标记是字段名称,则JsonParser的getCurrentName()方法将返回当前字段名称。

如果指向的令牌是字符串字段值,则getValueAsString()返回当前令牌值作为字符串。 如果指向的令牌是整数字段值,则getValueAsInt()返回当前令牌值作为int值。 JsonParser具有更多类似的方法来获取不同类型的curren令牌值(例如boolean,short,long,float,double等)。


六、JsonGenerator

Jackson JsonGenerator用于从Java对象(或代码从中生成JSON的任何数据结构)生成JSON。


1、创建一个JsonGenerator

为了创建Jackson JsonGenerator,必须首先创建JsonFactory实例。 这是创建JsonFactory的方法:

JsonFactory factory = new JsonFactory();

一旦创建了JsonFactory,就可以使用JsonFactory的createGenerator()方法创建JsonGenerator。 这是创建JsonGenerator的示例:

JsonFactory factory = new JsonFactory();JsonGenerator generator = factory.createGenerator(new File("data/output.json"), JsonEncoding.UTF8);

createGenerator()方法的第一个参数是生成的JSON的目标。 在上面的示例中,参数是File对象。 这意味着生成的JSON将被写入给定文件。 createGenerator()方法已重载,因此还有其他版本的createGenerator()方法采用例如OutputStream等,提供了有关将生成的JSON写入何处的不同选项。

createGenerator()方法的第二个参数是生成JSON时使用的字符编码。 上面的示例使用UTF-8。


2、使用JsonGenerator生成JSON

一旦创建了JsonGenerator,就可以开始生成JSON。 JsonGenerator包含一组write ...()方法,可以使用这些方法来编写JSON对象的各个部分。 这是一个使用Jackson JsonGenerator生成JSON的简单示例:

JsonFactory factory = new JsonFactory();JsonGenerator generator = factory.createGenerator(new File("data/output.json"), JsonEncoding.UTF8);generator.writeStartObject();
generator.writeStringField("brand", "Mercedes");
generator.writeNumberField("doors", 5);
generator.writeEndObject();generator.close();

此示例首先调用writeStartObject(),将{写入输出。 然后,该示例调用writeStringField(),将品牌字段名称+值写入输出。 之后,将调用writeNumberField()方法,此方法会将Doors字段名称+值写入输出。 最后,调用writeEndObject(),将}写入输出。

JsonGenerator还可以使用许多其他写入方法。 这个例子只显示了其中一些。


3、关闭JsonGenerator

完成生成JSON后,应关闭JsonGenerator。 您可以通过调用其close()方法来实现。 这是关闭JsonGenerator的样子:

generator.close();


七、Jackson注解

Jackson JSON工具包包含一组Java注解,可以使用这些注解来设置将JSON读入对象的方式或从对象生成什么JSON的方式。 此Jackson注解教程介绍了如何使用Jackson的注解。

下面是一些常用的注解:


注解用法
@JsonProperty用于属性,把属性的名称序列化时转换为另外一个名称。示例:
@JsonProperty("birth_ d ate")
private Date birthDate;
@JsonFormat用于属性或者方法,把属性的格式序列化时转换成指定的格式。示例:
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm")
public Date getBirthDate()
@JsonPropertyOrder用于类, 指定属性在序列化时 json 中的顺序 , 示例:
@JsonPropertyOrder({ "birth_Date", "name" })
public class Person
@JsonCreator用于构造方法,和 @JsonProperty 配合使用,适用有参数的构造方法。 示例:
@JsonCreator
public Person(@JsonProperty("name")String name) {…}
@JsonAnySetter用于属性或者方法,设置未反序列化的属性名和值作为键值存储到 map 中
@JsonAnySetter
public void set(String key, Object value) {
map.put(key, value);
}
@JsonAnyGetter用于方法 ,获取所有未序列化的属性
public Map any() { return map; }

下面是一些注解的详细说明。


一)、Read + Write注解

Jackson包含一组注解,这些注解会影响从JSON读取Java对象以及将Java对象写入JSON。 我将这些注解称为“读+写注解”。 以下各节将更详细地介绍Jackson的读写注解。


1、@JsonIgnore

Jackson注解@JsonIgnore用于告诉Jackson忽略Java对象的某个属性(字段)。 在将JSON读取到Java对象中以及将Java对象写入JSON时,都将忽略该属性。

这是使用@JsonIgnore注解的示例:

import com.fasterxml.jackson.annotation.JsonIgnore;public class PersonIgnore {@JsonIgnorepublic long personId = 0;public String name = null;
}

在上面的类中,不会从JSON读取或写入JSON属性personId。


2、@JsonIgnoreProperties

@JsonIgnoreProperties Jackson注解用于指定要忽略的类的属性列表。 @JsonIgnoreProperties注解放置在类声明上方,而不是要忽略的各个属性(字段)上方。

这是如何使用@JsonIgnoreProperties注解的示例:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;@JsonIgnoreProperties({"firstName", "lastName"})
public class PersonIgnoreProperties {public long personId = 0;public String firstName = null;public String lastName = null;}

在此示例中,属性firstName和lastName都将被忽略,因为它们的名称在类声明上方的@JsonIgnoreProperties注解声明内列出。


3、@JsonIgnoreType

@JsonIgnoreType Jackson注解用于将整个类型(类)标记为在使用该类型的任何地方都将被忽略。

这是一个示例,展示如何使用@JsonIgnoreType注解:

import com.fasterxml.jackson.annotation.JsonIgnoreType;public class PersonIgnoreType {@JsonIgnoreTypepublic static class Address {public String streetName = null;public String houseNumber = null;public String zipCode = null;public String city = null;public String country = null;}public long personId = 0;public String name = null;public Address address = null;
}

在上面的示例中,所有Address实例将被忽略。


4、@JsonAutoDetect

Jackson注解@JsonAutoDetect用于告诉Jackson在读写对象时包括非public修饰的属性。

这是一个示例类,展示如何使用@JsonAutoDetect注解:

import com.fasterxml.jackson.annotation.JsonAutoDetect;@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY )
public class PersonAutoDetect {private long personId = 123;public String name = null;}

JsonAutoDetect.Visibility类包含与Java中的可见性级别匹配的常量,表示ANY,DEFAULT,NON_PRIVATE,NONE,PROTECTED_AND_PRIVATE和PUBLIC_ONLY。


二)、Read注解

Jackson包含一组注解,这些注解仅影响Jackson将JSON解析为对象的方式-意味着它们影响Jackson对JSON的读取。 我称这些为“读注解”。 以下各节介绍了Jackson的读注解。


1、@JsonSetter

Jackson注解@JsonSetter用于告诉Jackson,当将JSON读入对象时,应将此setter方法的名称与JSON数据中的属性名称匹配。 如果Java类内部使用的属性名称与JSON文件中使用的属性名称不同,这个注解就很有用了。

以下Person类用personId名称对应JSON中名为id的字段:

public class Person {private long personId = 0;private String name = null;public long getPersonId() { return this.personId; }public void setPersonId(long personId) { this.personId = personId; }public String getName() { return name; }public void setName(String name) { this.name = name; }
}

但是在此JSON对象中,使用名称id代替personId:

{"id" : 1234,"name" : "John"
}

Jackson无法将id属性从JSON对象映射到Java类的personId字段。

@JsonSetter注解指示Jackson为给定的JSON字段使用setter方法。 在我们的示例中,我们在setPersonId()方法上方添加@JsonSetter注解。

这是添加@JsonSetter注解的实例:

public class Person {private long personId = 0;private String name = null;public long getPersonId() { return this.personId; }@JsonSetter("id")public void setPersonId(long personId) { this.personId = personId; }public String getName() { return name; }public void setName(String name) { this.name = name; }
}

@JsonSetter注解中指定的值是要与此setter方法匹配的JSON字段的名称。 在这种情况下,名称为id,因为这是我们要映射到setPersonId()setter方法的JSON对象中字段的名称。


2、@JsonAnySetter

Jackson注解@JsonAnySetter表示Jackson为JSON对象中所有无法识别的字段调用相同的setter方法。 “无法识别”是指尚未映射到Java对象中的属性或设置方法的所有字段。

看一下这个Bag类:

public class Bag {private Map properties &#61; new HashMap<>();public void set(String fieldName, Object value){this.properties.put(fieldName, value);}public Object get(String fieldName){return this.properties.get(fieldName);}
}

然后查看此JSON对象&#xff1a;

{"id" : 1234,"name" : "John"
}

Jackson无法直接将此JSON对象的id和name属性映射到Bag类&#xff0c;因为Bag类不包含任何公共字段或setter方法。

可以通过添加&#64;JsonAnySetter注解来告诉Jackson为所有无法识别的字段调用set()方法&#xff0c;如下所示&#xff1a;

public class Bag {private Map properties &#61; new HashMap<>();&#64;JsonAnySetterpublic void set(String fieldName, Object value){this.properties.put(fieldName, value);}public Object get(String fieldName){return this.properties.get(fieldName);}
}

现在&#xff0c;Jackson将使用JSON对象中所有无法识别的字段的名称和值调用set()方法。

请记住&#xff0c;这仅对无法识别的字段有效。 例如&#xff0c;如果您向Bag Java类添加了公共名称属性或setName&#xff08;String&#xff09;方法&#xff0c;则JSON对象中的名称字段将改为映射到该属性/设置器。


3、&#64;JsonCreator

Jackson注解&#64;JsonCreator用于告诉Jackson该Java对象具有一个构造函数&#xff08;“创建者”&#xff09;&#xff0c;该构造函数可以将JSON对象的字段与Java对象的字段进行匹配。

&#64;JsonCreator注解在无法使用&#64;JsonSetter注解的情况下很有用。 例如&#xff0c;不可变对象没有任何设置方法&#xff0c;因此它们需要将其初始值注入到构造函数中。

以这个PersonImmutable类为例&#xff1a;

public class PersonImmutable {private long id &#61; 0;private String name &#61; null;public PersonImmutable(long id, String name) {this.id &#61; id;this.name &#61; name;}public long getId() {return id;}public String getName() {return name;}}

要告诉Jackson应该调用PersonImmutable的构造函数&#xff0c;我们必须在构造函数中添加&#64;JsonCreator注解。 但是&#xff0c;仅凭这一点还不够。 我们还必须注解构造函数的参数&#xff0c;以告诉Jackson将JSON对象中的哪些字段传递给哪些构造函数参数。

添加了&#64;JsonCreator和&#64;JsonProperty注解的PersonImmutable类的示例如下&#xff1a;

public class PersonImmutable {private long id &#61; 0;private String name &#61; null;&#64;JsonCreatorpublic PersonImmutable(&#64;JsonProperty("id") long id,&#64;JsonProperty("name") String name ) {this.id &#61; id;this.name &#61; name;}public long getId() {return id;}public String getName() {return name;}}

请注意&#xff0c;构造函数上方的注解以及构造函数参数之前的注解。 现在&#xff0c;Jackson能够从此JSON对象创建PersonImmutable&#xff1a;

{"id" : 1234,"name" : "John"
}


4、&#64;JacksonInject

Jackson注解&#64;JacksonInject用于将值注入到解析的对象中&#xff0c;而不是从JSON中读取这些值。 例如&#xff0c;假设正在从各种不同的源下载Person JSON对象&#xff0c;并且想知道给定Person对象来自哪个源。 源本身可能不包含该信息&#xff0c;但是可以让Jackson将其注入到根据JSON对象创建的Java对象中。

要将Java类中的字段标记为需要由Jackson注入其值的字段&#xff0c;请在该字段上方添加&#64;JacksonInject注解。

这是一个示例PersonInject类&#xff0c;在属性上方添加了&#64;JacksonInject注解&#xff1a;

public class PersonInject {public long id &#61; 0;public String name &#61; null;&#64;JacksonInjectpublic String source &#61; null;}

为了让Jackson将值注入属性&#xff0c;需要在创建Jackson ObjectMapper时做一些额外的工作。

这是让Jackson将值注入Java对象的过程&#xff1a;

InjectableValues inject &#61; new InjectableValues.Std().addValue(String.class, "jenkov.com");
PersonInject personInject &#61; new ObjectMapper().reader(inject).forType(PersonInject.class).readValue(new File("data/person.json"));

请注意&#xff0c;如何在InjectableValues addValue()方法中设置要注入到source属性中的值。 还要注意&#xff0c;该值仅绑定到字符串类型-而不绑定到任何特定的字段名称。 &#64;JacksonInject注解指定将值注入到哪个字段。

如果要从多个源下载人员JSON对象&#xff0c;并为每个源注入不同的源值&#xff0c;则必须为每个源重复以上代码。


5、&#64;JsonDeserialize

Jackson注解&#64;JsonDeserialize用于为Java对象中给定的属性指定自定义反序列化器类。

例如&#xff0c;假设想优化布尔值false和true的在线格式&#xff0c;使其分别为0和1。

首先&#xff0c;需要将&#64;JsonDeserialize注解添加到要为其使用自定义反序列化器的字段。 这是将&#64;JsonDeserialize注解添加到字段的示例&#xff1a;

public class PersonDeserialize {public long id &#61; 0;public String name &#61; null;&#64;JsonDeserialize(using &#61; OptimizedBooleanDeserializer.class)public boolean enabled &#61; false;
}

其次&#xff0c;这是&#64;JsonDeserialize注解中引用的OptimizedBooleanDeserializer类的实例&#xff1a;

public class OptimizedBooleanDeserializerextends JsonDeserializer {&#64;Overridepublic Boolean deserialize(JsonParser jsonParser,DeserializationContext deserializationContext) throwsIOException, JsonProcessingException {String text &#61; jsonParser.getText();if("0".equals(text)) return false;return true;}
}

请注意&#xff0c;OptimizedBooleanDeserializer类使用通用类型Boolean扩展了JsonDeserializer。 这样做会使deserialize()方法返回一个布尔对象。 如果要反序列化其他类型&#xff08;例如java.util.Date&#xff09;&#xff0c;则必须在泛型括号内指定该类型。

可以通过调用jsonParser参数的getText()方法来获取要反序列化的字段的值。 然后&#xff0c;可以将该文本反序列化为任何值&#xff0c;然后输入反序列化程序所针对的类型&#xff08;在此示例中为布尔值&#xff09;。

最后&#xff0c;需要查看使用自定义反序列化器和&#64;JsonDeserializer注解反序列化对象的格式&#xff1a;

PersonDeserialize person &#61; objectMapper.reader(PersonDeserialize.class).readValue(new File("data/person-optimized-boolean.json"));

注意&#xff0c;我们首先需要如何使用ObjectMapper的reader()方法为PersonDeserialize类创建一个阅读器&#xff0c;然后在该方法返回的对象上调用readValue()。


三&#xff09;、Write注解

Jackson还包含一组注解&#xff0c;这些注解可以影响Jackson将Java对象序列化&#xff08;写入&#xff09;到JSON的方式。 以下各节将介绍这些写&#xff08;序列化&#xff09;注解中的每一个。


1、&#64;JsonInclude

Jackson注解&#64;JsonInclude告诉Jackson仅在某些情况下包括属性。 例如&#xff0c;仅当属性为非null&#xff0c;非空或具有非默认值时&#xff0c;才应包括该属性。 这是显示如何使用&#64;JsonInclude注解的示例&#xff1a;

import com.fasterxml.jackson.annotation.JsonInclude;&#64;JsonInclude(JsonInclude.Include.NON_EMPTY)
public class PersonInclude {public long personId &#61; 0;public String name &#61; null;}

如果为该示例设置的值是非空的&#xff0c;则此示例将仅包括name属性&#xff0c;这意味着不为null且不是空字符串。

&#64;JsonInclude注解的一个更通俗的名称应该是&#64;JsonIncludeOnlyWhen&#xff0c;但是写起来会更长。


2、&#64;JsonGetter

&#64;JsonGetter Jackson注解用于告诉Jackson&#xff0c;应该通过调用getter方法而不是通过直接字段访问来获取某个字段值。 如果您的Java类使用jQuery样式的getter和setter名称&#xff0c;则&#64;JsonGetter注解很有用。

例如&#xff0c;您可能拥有方法personId()和personId&#xff08;long id&#xff09;&#xff0c;而不是getPersonId()和setPersonId()。

这是一个名为PersonGetter的示例类&#xff0c;它显示了&#64;JsonGetter注解的用法&#xff1a;

public class PersonGetter {private long personId &#61; 0;&#64;JsonGetter("id")public long personId() { return this.personId; }&#64;JsonSetter("id")public void personId(long personId) { this.personId &#61; personId; }}

如您所见&#xff0c;personId()方法带有&#64;JsonGetter注解。 &#64;JsonGetter注解上设置的值是JSON对象中应使用的名称。 因此&#xff0c;用于JSON对象中personId的名称是id。 生成的JSON对象如下所示&#xff1a;

{"id":0}

还要注意&#xff0c;personId&#xff08;long personId&#xff09;方法使用&#64;JsonSetter注解进行注解&#xff0c;以使Jackson识别为与JSON对象中的id属性匹配的设置方法。 从JSON读取Java对象时使用&#64;JsonSetter注解-将Java对象写入JSON时不使用。 为了完整起见&#xff0c;仅包含&#64;JsonSetter注解。


3、&#64;JsonAnyGetter

&#64;JsonAnyGetter Jackson注解使您可以将Map用作要序列化为JSON的属性的容器。 这是在Java类中使用&#64;JsonAnyGetter注解的示例&#xff1a;

public class PersonAnyGetter {private Map properties &#61; new HashMap<>();&#64;JsonAnyGetterpublic Map properties() {return properties;}
}

当看到&#64;JsonAnyGetter注解时&#xff0c;Jackson将从&#64;JsonAnyGetter注解的方法中获取返回的Map&#xff0c;并将该Map中的每个键值对都视为一个属性。 换句话说&#xff0c;Map中的所有键值对都将作为PersonAnyGetter对象的一部分序列化为JSON。


4、&#64;JsonPropertyOrder

&#64;JsonPropertyOrder Jackson注解可用于指定将Java对象的字段序列化为JSON的顺序。 这是显示如何使用&#64;JsonPropertyOrder注解的示例&#xff1a;

&#64;JsonPropertyOrder({"name", "personId"})
public class PersonPropertyOrder {public long personId &#61; 0;public String name &#61; null;}

通常&#xff0c;Jackson会按照在类中找到的顺序序列化PersonPropertyOrder中的属性。 但是&#xff0c;&#64;JsonPropertyOrder注解指定了不同的顺序&#xff0c;在序列化的JSON输出中&#xff0c;name属性将首先出现&#xff0c;personId属性将随后出现。


5、&#64;JsonRawValue

&#64;JsonRawValue Jackson注解告诉Jackson该属性值应直接写入JSON输出。 如果该属性是字符串&#xff0c;Jackson通常会将值括在引号中&#xff0c;但是如果使用&#64;JsonRawValue属性进行注解&#xff0c;Jackson将不会这样做。

为了更清楚&#64;JsonRawValue的作用&#xff0c;看看没有使用&#64;JsonRawValue的此类&#xff1a;

public class PersonRawValue {public long personId &#61; 0;public String address &#61; "$#";
}

Jackson会将其序列化为以下JSON字符串&#xff1a;

{"personId":0,"address":"$#"}

现在&#xff0c;我们将&#64;JsonRawValue添加到address属性&#xff0c;如下所示&#xff1a;

public class PersonRawValue {public long personId &#61; 0;&#64;JsonRawValuepublic String address &#61; "$#";
}

现在&#xff0c;当对地址属性进行序列化时&#xff0c;杰克逊将省略引号。 因此&#xff0c;序列化的JSON如下所示&#xff1a;

{"personId":0,"address":$#}

当然它是无效的JSON&#xff0c;那么为什么要这么做呢&#xff1f;

如果address属性包含一个JSON字符串&#xff0c;那么该JSON字符串将被序列化为最终的JSON对象&#xff0c;作为JSON对象结构的一部分&#xff0c;而不仅是序列化为JSON对象的address字段中的字符串。

要查看其工作原理&#xff0c;让我们像下面这样更改address属性的值&#xff1a;

public class PersonRawValue {public long personId &#61; 0;&#64;JsonRawValuepublic String address &#61;"{ \"street\" : \"Wall Street\", \"no\":1}";}

Jackson会将其序列化为以下JSON&#xff1a;

{"personId":0,"address":{ "street" : "Wall Street", "no":1}}

请注意&#xff0c;JSON字符串现在如何成为序列化JSON结构的一部分。

没有&#64;JsonRawValue注解&#xff0c;Jackson会将对象序列化为以下JSON&#xff1a;

{"personId":0,"address":"{ \"street\" : \"Wall Street\", \"no\":1}"}

请注意&#xff0c;address属性的值现在如何用引号引起来&#xff0c;并且值内的所有引号均被转义。


6、&#64;JsonValue

Jackson注解&#64;JsonValue告诉Jackson&#xff0c;Jackson不应该尝试序列化对象本身&#xff0c;而应在对象上调用将对象序列化为JSON字符串的方法。 请注意&#xff0c;Jackson将在自定义序列化返回的String内转义任何引号&#xff0c;因此不能返回例如 完整的JSON对象。 为此&#xff0c;应该改用&#64;JsonRawValue&#xff08;请参阅上一节&#xff09;。

&#64;JsonValue注解已添加到Jackson调用的方法中&#xff0c;以将对象序列化为JSON字符串。 这是显示如何使用&#64;JsonValue注解的示例&#xff1a;

public class PersonValue {public long personId &#61; 0;public String name &#61; null;&#64;JsonValuepublic String toJson(){return this.personId &#43; "," &#43; this.name;}}

要求Jackson序列化PersonValue对象所得到的输出是&#xff1a;

"0,null"

引号由Jackson添加。 请记住&#xff0c;对象返回的值字符串中的所有引号均会转义。


7、&#64;JsonSerialize

&#64;JsonSerialize Jackson注解用于为Java对象中的字段指定自定义序列化程序。 这是一个使用&#64;JsonSerialize注解的Java类示例&#xff1a;

public class PersonSerializer {public long personId &#61; 0;public String name &#61; "John";&#64;JsonSerialize(using &#61; OptimizedBooleanSerializer.class)public boolean enabled &#61; false;
}

注意启用字段上方的&#64;JsonSerialize注解。

OptimizedBooleanSerializer将序列的真值序列化为1&#xff0c;将假值序列化为0。这是代码&#xff1a;

public class OptimizedBooleanSerializer extends JsonSerializer {&#64;Overridepublic void serialize(Boolean aBoolean, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {if(aBoolean){jsonGenerator.writeNumber(1);} else {jsonGenerator.writeNumber(0);}}
}


转载
链接&#xff1a;https://juejin.cn/post/6844904166809157639
 


推荐阅读
  • Spring框架的核心组件与架构解析 ... [详细]
  • 提升Android开发效率:Clean Code的最佳实践与应用
    在Android开发中,提高代码质量和开发效率是至关重要的。本文介绍了如何通过Clean Code的最佳实践来优化Android应用的开发流程。以SQLite数据库操作为例,详细探讨了如何编写高效、可维护的SQL查询语句,并将其结果封装为Java对象。通过遵循这些最佳实践,开发者可以显著提升代码的可读性和可维护性,从而加快开发速度并减少错误。 ... [详细]
  • 在Java分层设计模式中,典型的三层架构(3-tier application)将业务应用细分为表现层(UI)、业务逻辑层(BLL)和数据访问层(DAL)。这种分层结构不仅有助于提高代码的可维护性和可扩展性,还能有效分离关注点,使各层职责更加明确。通过合理的设计和实现,三层架构能够显著提升系统的整体性能和稳定性。 ... [详细]
  • Squaretest:自动生成功能测试代码的高效插件
    本文将介绍一款名为Squaretest的高效插件,该工具能够自动生成功能测试代码。使用这款插件的主要原因是公司近期加强了代码质量的管控,对各项目进行了严格的单元测试评估。Squaretest不仅提高了测试代码的生成效率,还显著提升了代码的质量和可靠性。 ... [详细]
  • 第六章:枚举类型与switch结构的应用分析
    第六章深入探讨了枚举类型与 `switch` 结构在编程中的应用。枚举类型(`enum`)是一种将一组相关常量组织在一起的数据类型,广泛存在于多种编程语言中。例如,在 Cocoa 框架中,处理文本对齐时常用 `NSTextAlignment` 枚举来表示不同的对齐方式。通过结合 `switch` 结构,可以更清晰、高效地实现基于枚举值的逻辑分支,提高代码的可读性和维护性。 ... [详细]
  • 本文探讨了利用Python实现高效语音识别技术的方法。通过使用先进的语音处理库和算法,本文详细介绍了如何构建一个准确且高效的语音识别系统。提供的代码示例和实验结果展示了该方法在实际应用中的优越性能。相关文件可从以下链接下载:链接:https://pan.baidu.com/s/1RWNVHuXMQleOrEi5vig_bQ,提取码:p57s。 ... [详细]
  • 在本文中,我们将为 HelloWorld 项目添加视图组件,以确保控制器返回的视图路径能够正确映射到指定页面。这一步骤将为后续的测试和开发奠定基础。首先,我们将介绍如何配置视图解析器,以便 SpringMVC 能够识别并渲染相应的视图文件。 ... [详细]
  • 本文详细介绍了如何在Java Web服务器上部署音视频服务,并提供了完整的验证流程。以AnyChat为例,这是一款跨平台的音视频解决方案,广泛应用于需要实时音视频交互的项目中。通过具体的部署步骤和测试方法,确保了音视频服务的稳定性和可靠性。 ... [详细]
  • Java解析YAML文件并转换为JSON格式(支持JSON与XML的结构化查询)
    本文探讨了如何利用Java解析YAML文件并将其转换为JSON格式,同时支持JSON和XML的结构化查询。YAML、JSON和XML这三种数据格式通过其名称作为文件扩展名,便于区分和使用。文章详细介绍了这些格式的层次结构和数据表示方法,并重点讨论了在数据传输过程中,XML的特性和优势。此外,还提供了具体的代码示例和实现步骤,帮助开发者高效地进行数据格式转换和查询操作。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • 在实际开发中,连接池是最常使用的数据库管理技术之一。连接池通过创建和管理一组预初始化的数据库连接,使得这些连接可以被多个线程复用,从而显著提高应用程序的性能和资源利用率。本文将详细介绍如何从零开始构建一个自定义的 MySQL 连接池,并将其与 Spring Data 进行集成,以实现高效、可靠的数据库操作。 ... [详细]
  • 如何在IntelliJ IDEA中生成Maven项目的所有Jar包依赖关系图
    本文详细介绍了如何在IntelliJ IDEA中生成Maven项目的完整Jar包依赖关系图。通过具体步骤和示例,帮助开发者清晰地理解并掌握这一重要功能,适合希望深入了解Maven依赖管理的读者学习参考。 ... [详细]
  • 在近期的项目开发过程中,ORM层采用了MyBatis,并且需要连接多个数据库,这带来了多数据源配置的挑战。为了解决这一问题,我们可以通过巧妙运用注解来实现优雅的数据源切换,确保系统的灵活性和可维护性。这种方法不仅简化了配置,还提高了代码的可读性和扩展性。 ... [详细]
  • 本文推荐了六款高效的Java Web应用开发工具,并详细介绍了它们的实用功能。其中,分布式敏捷开发系统架构“zheng”项目,基于Spring、Spring MVC和MyBatis技术栈,提供了完整的分布式敏捷开发解决方案,支持快速构建高性能的企业级应用。此外,该工具还集成了多种中间件和服务,进一步提升了开发效率和系统的可维护性。 ... [详细]
  • Spring框架入门指南:专为新手打造的详细学习笔记
    Spring框架是Java Web开发中广泛应用的轻量级应用框架,以其卓越的功能和出色的性能赢得了广大开发者的青睐。本文为初学者提供了详尽的学习指南,涵盖基础概念、核心组件及实际应用案例,帮助新手快速掌握Spring框架的核心技术与实践技巧。 ... [详细]
author-avatar
EvaMa奕文产_799
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有