Jackson学习笔记(详细)
学习地址:http://tutorials.jenkov.com/java-json/index.html
github地址:https://github.com/FasterXML/jackson
fasterxml官方地址:http://fasterxml.com/
Jackson ObjectMapper
解析器和生成器
jackson有两个json解析器
- ObjectMapper将Json解析为Java对象或jackson的tree model
- JsonParser,一次解析一个token
jackson有两个json生成器
- ObjectMapper从java对象或tree model生成json
- JsonGenerator,一次生成一个token
如何映射
jackson通过映射java对象的getter和setter方法实现与json串字段的对应,去掉get set,然后将第一个字母小写,如果要实现别的对应,需要定制序列化与反序列化,或者使用一些jackson注解。
jackson可以读取的内容
jackson读取对象、对象数组可以从
- json 字符串
- Reader
- json file
- URL,包括HTTP URL
- InputStream
- ByteArray
- 需要使用new TypeReference<List<Car>>(){}的复杂对象的解析
- 通过json数组读取对象数组
- 通过json数组读取对象List
- 通过json字符串读取map
jackson的配置
// 忽略额外的json字段,默认会抛出异常 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); //null值赋值给java原生类型时抛出异常 objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
定制反序列化器
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);
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; public class CarDeserializer extends StdDeserializer<Car> { public CarDeserializer(Class<?> vc) { super(vc); } @Override public 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
writeValue()
writeValueAsString()
writeValueAsBytes()
定制序列化器
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);
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; public class CarSerializer extends StdSerializer<Car> { protected CarSerializer(Class<Car> 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(); } }
修改默认的日期序列化格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); objectMapper2.setDateFormat(dateFormat); String output2 = objectMapper2.writeValueAsString(transaction); System.out.println(output2);
json树模型 JsonNode类
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }"; ObjectMapper objectMapper = new ObjectMapper(); try { JsonNode jsonNode = objectMapper.readTree(carJson); } catch (IOException e) { e.printStackTrace(); }
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(); }
java对象与JsonNode的转换
ObjectMapper objectMapper = new ObjectMapper(); Car car = new Car(); car.brand = "Cadillac"; car.doors = 4; JsonNode carJsonNode = objectMapper.valueToTree(car);
ObjectMapper objectMapper = new ObjectMapper(); String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }"; JsonNode carJsonNode = objectMapper.readTree(carJson); Car car = objectMapper.treeToValue(carJsonNode);
ObjectMapper读取其他的数据格式
- CBOR
- MessagePack
- YAML
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import java.io.IOException; public class YamlJacksonExample { public static void main(String[] args) { ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); Employee employee = new Employee("John Doe", "john@doe.com"); String yamlString = null; try { yamlString = objectMapper.writeValueAsString(employee); } catch (JsonProcessingException e) { e.printStackTrace(); // normally, rethrow exception here - or don't catch it at all. } try { Employee employee2 = objectMapper.readValue(yamlString, Employee.class); System.out.println("Done"); } catch (IOException e) { e.printStackTrace(); } } }
Jackson JsonNode
JsonNode是不可变的,子类ObjectNode是可变的
将json读取为JsonNode,写为json
JsonNode jsonNode = objectMapper.readTree(json); String json = objectMapper.writeValueAsString(jsonNode);
获取字段
sonNode jsonNode = ... //parse above JSON into a JsonNode JsonNode field1 = jsonNode.get("field1"); JsonNode field2 = jsonNode.get("field2"); JsonNode nameNode = jsonNode.at("/identification/name"); //开头必须有/
转化字段的值(注意如果该字段会导致空指针异常)
String f2Str = jsonNode.get("f2").asText(); double f2Dbl = jsonNode.get("f2").asDouble(); int f2Int = jsonNode.get("f2").asInt(); long f2Lng = jsonNode.get("f2").asLong();
提供默认值
ObjectMapper objectMapper = new ObjectMapper(); String json = "{ \"f1\":\"Hello\", \"f2\":null }"; JsonNode jsonNode = objectMapper.readTree(json); String f2Value = jsonNode.get("f2").asText("Default");
判断是否是有效的JsonNode
JsonNode fieldNode = parentNode.get("fieldName"); if(fieldNode == null || fieldNode.isNull()) { // the field is either not present in parentNode, or explicitly set to null . }
递归遍历整个JsonNode
public static void traverse(JsonNode root){ if(root.isObject()){ Iterator<String> fieldNames = root.fieldNames(); while(fieldNames.hasNext()) { String fieldName = fieldNames.next(); JsonNode fieldValue = root.get(fieldName); traverse(fieldValue); } } else if(root.isArray()){ ArrayNode arrayNode = (ArrayNode) root; for(int i = 0; i < arrayNode.size(); i++) { JsonNode arrayElement = arrayNode.get(i); traverse(arrayElement); } } else { // JsonNode root represents a single value field - do something with it. } }
创建ObjectNode,设置值,删除值
//创建 ObjectMapper objectMapper = new ObjectMapper(); ObjectNode parentNode = objectMapper.createObjectNode(); JsonNode childNode = readJsonIntoJsonNode(); parentNode.set("child1", childNode); //直接赋值原生类型 objectNode.put("field1", "value1"); objectNode.put("field2", 123); objectNode.put("field3", 999.999); //删除值 objectNode.remove("fieldName");
遍历JsonNode的字段
Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields(); while(fields.hasNext()) { Map.Entry<String, JsonNode> field = fields.next(); String fieldName = field.getKey(); JsonNode fieldValue = field.getValue(); System.out.println(fieldName + " = " + fieldValue.asText()); }
遍历字段名
Iterator<String> fieldNames = jsonNode.fieldNames(); while(fieldNames.hasNext()) { String fieldName = fieldNames.next(); JsonNode field = jsonNode.get(fieldName); }
Jackson JsonParser
JsonParser相对于ObjectMapper是low-level的Json解析器,因此也更快,但也更笨重。通过解析器解析就是将json分解为一个一个的token,然后迭代
创建解析器,解析(也可以直接从objectMapper创建)
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);
token的类型有以下几种,可以通过这些常量找出当前token的类型
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
Jackson JsonGenerator
创建Json生成器、生成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();
Jackson Annotations
- Read + Write Annotations
- @JsonIgnore 忽略该属性的序列化和反序列化
- @JsonIgnoreProperties 忽略一组属性的序列化和反序列化,放在类上,@JsonIgnoreProperties({"firstName", "lastName"})
- @JsonIgnoreType 忽略该类型的属性(所有出现的地方)
- @JsonAutoDetect 包含非public的字段,放在类上,@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY )
- Read Annotations
- @JsonSetter 放在setter方法上,修改读取的json字段名,@JsonSetter("id")
- @JsonAnySetter 对于未识别的Json字段将调用该注解放的set方法上
- @JsonCreator @JsonAnySetter不可以用的时候用,用该注解标注的构造器创建,也需要标注哪些参数需要构造
- @JacksonInject 通过注入的方式赋值该字段的值
- @JsonDeserialize 指定定制的反序列化器
- Write Annotations
- @JsonInclude 放在类上,指定非空才序列化@JsonInclude(JsonInclude.Include.NON_EMPTY),
@JsonInclude(Include.NON_NULL)指定非null才序列化
- @JsonGetter 修改序列化的字段名
- @JsonAnyGetter 将该注解标注的方法返回的值其中的kv单独拿出来序列化
- @JsonPropertyOrder 放在类上,指定字段序列化的顺序
- @JsonRawValue 该注解标注的字段会去掉字符串的引号进行序列化
- @JsonValue 序列化时不序列化对象,而是调用该注解标注的方法
- @JsonSerialize 指定序列化器
- @JsonInclude 放在类上,指定非空才序列化@JsonInclude(JsonInclude.Include.NON_EMPTY),
总结:
- Java对象到Json字符串:objectMapper.writeValueAsString(user)
- 可以定制序列化器
- Json到Java对象
- 简单类型:objectMapper.readValue(jsonString, User.class);
- 复杂类型:objectMapper.readValue(jsonString, new TypeReference<List<User>>(){});
- 可以定制反序列化器,Custom Deserializer
-
核心模块(三个)
databind依赖于streaming和annotations,使用这一个就可以了
地址:https://github.com/FasterXML/jackson-databind,doc见wiki
-
介绍
其他格式的也支持,只要实现了解析器和生成器
mvn仓库
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.4</version> </dependency>
如果使用java8的LocalDateTime需要引入jackson-datatype-jsr310(依赖于databind)的maven,且需要使用objectMapper.findAndRegisterModules()代码注册模块。
-
简单使用
//Java对象转为JSON字符串 user = new User(); objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); //使用java8的localDateTime时使用,但是日期还是有问题,需要在日期的属性上加@JsonFormat(paattern = "yyyy-MM-dd HH:mm:ss") String jsonString = objectMapper.writeValueAsString(user); //返回json字符串 //JSON字符串转为Java对象 objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); objectMapper.readValue(jsonString, User.class); //简单类型, 有对应的实体类, 如果有泛型会出问题,反序列化的时候会把泛型丢掉, 使用new TypeReference<复杂类型>(){} objectMapper.readValue(jsonString, new TypeReference<List<User>>(){}); //复杂类型 //修改属性名 @JsonProperty("changedName") //在属性上加 //忽略属性 @JsonIgnore //在属性上加 @JsonIgnoreProperties({"name1", "name2"}) //在类上加
-
复杂类型使用
/** * 复杂类型转换1 */ @Test public void test() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); User2 user2 = new User2(); user2.setId(1001); user2.setName("zhangsan"); ArrayList<Account> accList = new ArrayList<>(); Account acc1 = new Account(100, "100"); Account acc2 = new Account(200, "200"); accList.add(acc1); accList.add(acc2); HashMap<String, Account> accMap = new HashMap<>(); accMap.put("zhang10", acc1); accMap.put("zhang20", acc2); user2.setAccList(accList); user2.setAccMap(accMap); String s = mapper.writeValueAsString(user2); System.out.println(s); User2 user21 = mapper.readValue(s, User2.class); System.out.println(user21); } /** * 复杂类型转换2 * List, Map转换, 简单类型可以直接使用,复杂类型使用TypeReference */ @Test public void test02() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); ArrayList<User> list = new ArrayList<>(); User u1 = new User(1001, "zhang1001"); User u2 = new User(1002, "zhang1002"); list.add(u1); list.add(u2); String s = mapper.writeValueAsString(list); System.out.println(s); List list1 = mapper.readValue(s, List.class); System.out.println(list1); // System.out.println("----------------------"); HashMap<String, User> map = new HashMap<>(); map.put("10", u1); map.put("20", u2); String s1 = mapper.writeValueAsString(map); System.out.println(s1); Map map1 = mapper.readValue(s1, Map.class); //返回值没有泛型信息 System.out.println(map1); System.out.println("--------"); Object o = map1.get("10"); //强转报错 // User o = (User) map1.get("10"); //强转报错 Map<String, User> map2 = mapper.readValue(s1, new TypeReference<Map<String, User>>() { }); System.out.println(map2); User user = map2.get("10"); System.out.println(user); }
-
Tree Model
将Json转化为树结构
/** * 使用 tree 模型, 类似于JSONObject */ @Test public void test03() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); User user = new User(1001, null); String s = mapper.writeValueAsString(user); JsonNode node = mapper.readTree(s); System.out.println(node.toString()); System.out.println("------------"); JsonNode jsonNode = node.get("id"); int id = node.get("id").asInt(); String name = node.get("name").asText(); String name1 = node.get("name").asText("123"); System.out.println(name); System.out.println(name1); System.out.println(id); }
-
Streaming parser, generator
流处理
/** * Streaming parser, generator * 将json写入流 */ @Test public void test04() throws IOException { ObjectMapper mapper = new ObjectMapper(); File jsonfile = new File("test.json"); JsonGenerator g = mapper.createGenerator(jsonfile, JsonEncoding.UTF8); g.writeStartObject(); g.writeStringField("message", "hello world"); g.writeEndObject(); g.close(); boolean newFile = jsonfile.createNewFile(); //写入文件 //从流写回 JsonParser p = mapper.createParser(jsonfile); JsonToken t = p.nextToken(); t = p.nextToken(); if ((t != JsonToken.FIELD_NAME) || (!"message".equals(p.getCurrentName()))) { System.out.println("handle error"); } t = p.nextToken(); if (t != JsonToken.VALUE_STRING) { System.out.println("handle error.."); } String text = p.getText(); System.out.println("my message: " + text); }
-
配置(特性、注解)
所有配置:https://github.com/FasterXML/jackson-databind/wiki/JacksonFeatures
特性配置(higher-level data-binding configuration)
//序列化 // SerializationFeature for changing how JSON is written // to enable standard indentation ("pretty-printing"): mapper.enable(SerializationFeature.INDENT_OUTPUT); //开启序列化标准缩进 // to allow serialization of "empty" POJOs (no properties to serialize) // (without this setting, an exception is thrown in those cases) mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); //关闭空bean序列化异常, 允许空bean的序列化 // to write java.util.Date, Calendar as number (timestamp): mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); //关闭date序列化为时间戳 //反序列化 // DeserializationFeature for changing how JSON is read as POJOs: // to prevent exception when encountering unknown property: mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); //关闭反序列化未知属性的异常 // to allow coercion of JSON empty String ("") to null Object value: mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); //开启空字符串反序列化为null
特性配置(low-level JSON parsing, generation details)
//解析json特性 // JsonParser.Feature for configuring parsing settings: // to allow C/C++ style comments in JSON (non-standard, disabled by default) // (note: with Jackson 2.5, there is also `mapper.enable(feature)` / `mapper.disable(feature)`) mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); //允许注释的存在 // to allow (non-standard) unquoted field names in JSON: mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); //允许字段名未使用双引号 // to allow use of apostrophes (single quotes), non standard mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); //允许字段名使用单引号 //生成json特性 // JsonGenerator.Feature for configuring low-level JSON generation: // to force escaping of non-ASCII characters: mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true); //强制生成非ascii码字符
注解
public class MyBean { private String _name; // without annotation, we'd get "theName", but we want "name": @JsonProperty("name") public String getTheName() { return _name; } // note: it is enough to add annotation on just getter OR setter; // so we can omit it here public void setTheName(String n) { _name = n; } }
// means that if we see "foo" or "bar" in JSON, they will be quietly skipped // regardless of whether POJO has such properties @JsonIgnoreProperties({ "foo", "bar" }) public class MyBean { // will not be written as JSON; nor assigned from JSON: @JsonIgnore public String internal; // no annotation, public field is read/written normally public String external; @JsonIgnore public void setCode(int c) { _code = c; } // note: will also be ignored because setter has annotation! public int getCode() { return _code; } } public class ReadButDontWriteProps { private String _name; @JsonProperty public void setName(String n) { _name = n; } @JsonIgnore public String getName() { return _name; } }
使用定制构造器
public class CtorBean { public final String name; public final int age; @JsonCreator // constructor can be public, private, whatever private CtorBean(@JsonProperty("name") String name, @JsonProperty("age") int age) { this.name = name; this.age = age; } } public class FactoryBean { // fields etc omitted for brevity @JsonCreator public static FactoryBean create(@JsonProperty("name") String name) { // construct and return an instance } }
-
POJO到POJO的转换
直接转换,只要to-JSON,from-JSON正常就可以使用
-
使用建造者模式 + Jackson
在反序列化时指定建造者进行反序列化
本文来自博客园,作者:Bingmous,转载请注明原文链接:https://www.cnblogs.com/bingmous/p/15643669.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~