json
轻量级的数据交换格式,完全独立于编程语言的文本格式来存储和表示数据,简洁,清晰,容易解析,提高网络传输效率
json常用的解析库,jackson(SpringMVC),fastjson(阿里),gson(Google)
json的高级应用
https://www.cnblogs.com/EasonJim/p/8098921.html
springboot中使用jackson
https://blog.csdn.net/swordcenter/article/details/72368905
- Streaming流处理模块(
jackson-core
):定义底层处理流的API:JsonPaser和JsonGenerator等,并包含特定于json的实现
- Annotations标准注解模块(
jackson-annotations
):包含标准的Jackson注解
- Databind数据绑定模块(
jackson-databind
):在streaming包上实现数据绑定(和对象序列化)支持;它依赖于上面的两个模块,也是Jackson的高层API(如ObjectMapper)所在的模块
jackson-databind 依赖 jackson-core 和 jackson-annotations,添加jackson-databind即可
jackson和fastjson 配置基本差不多,但是jackson的没有module的配置
jackson-databind
<!-- jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
springboot中不需要导入包,web启动器里面包含了
ObjectMapper
JSON字符串->对象
ObjectMapper objectMapper = new ObjectMapper();
String carJson =
"{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
Car car = objectMapper.readValue(carJson, Car.class);
JSON 字符输入流->对象
ObjectMapper objectMapper = new ObjectMapper();
String carJson =
"{ \"brand\" : \"Mercedes\", \"doors\" : 4 }";
Reader reader = new StringReader(carJson);
Car car = objectMapper.readValue(reader, Car.class);
JSON文件->对象
ObjectMapper objectMapper = new ObjectMapper();
File file = new File("data/car.json");
Car car = objectMapper.readValue(file, Car.class);
JSON URL->对象
可以通过URL(java.net.URL)从JSON读取对象,如下所示:
ObjectMapper objectMapper = new ObjectMapper();
URL url = new URL("file:data/car.json");
Car car = objectMapper.readValue(url, Car.class);
JSON字节输入流->对象
ObjectMapper objectMapper = new ObjectMapper();
InputStream input = new FileInputStream("data/car.json");
Car car = objectMapper.readValue(input, Car.class);
JSON二进制数组->对象
ObjectMapper objectMapper = new ObjectMapper();
String carJson =
"{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
byte[] bytes = carJson.getBytes("UTF-8");
Car car = objectMapper.readValue(bytes, Car.class);
JSON数组字符串->对象数组
String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
Car[] cars2 = objectMapper.readValue(jsonArray, Car[].class);
JSON数组字符串->List
String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> cars1 = objectMapper.readValue(jsonArray, new TypeReference<List<Car>>(){});
JSON字符串->Map
String jsonObject = "{\"brand\":\"ford\", \"doors\":5}";
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(jsonObject,
new TypeReference<Map<String,Object>>(){});
ObjectReader
ObjectWriter
Module
https://www.hiczp.com/spring/zai-springboot-zhong-zheng-que-zhu-ce-jacksonmodule.html
配置ObjectMapper
https://www.cnblogs.com/scar1et/articles/14134024.html
忽略未知的JSON字段
objectMapper.configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
不允许基本类型为null
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
详细配置请看 SerializerFeature配置
配置Visibility
需要注意的是对于第二种通过配置SerializationConfig和DeserializationConfig方式只能启动/禁止自动检测,无法修改我们所需的可见级别
有时候对每个实例进行可见级别的注解可能会非常麻烦,这时候我们需要配置一个全局的可见级别,通过objectMapper.setVisibilityChecker()来实现,默认的VisibilityChecker实现类为VisibilityChecker.Std,这样可以满足实现复杂场景下的基础配置。
也有一些实用简单的可见级别配置,比如:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) // auto-detect all member fields
.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE) // but only public getters
.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE) // and none of "is-setters"
;
源码:
public ObjectMapper setVisibility(PropertyAccessor forMethod, Visibility visibility)
public enum PropertyAccessor {
GETTER,
SETTER,
CREATOR,
FIELD,
IS_GETTER,
NONE,
ALL;
// ...
}
public static enum Visibility {
ANY,
NON_PRIVATE,
PROTECTED_AND_PUBLIC,
PUBLIC_ONLY,
NONE,
DEFAULT;
// ...
}
你也可以通过下面方式来禁止所有的自动检测功能
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibilityChecker(objectMapper.getVisibilityChecker().with(JsonAutoDetect.Visibility.NONE));
配置enableDefaultTyping
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
public static enum DefaultTyping {
JAVA_LANG_OBJECT,
OBJECT_AND_NON_CONCRETE,
NON_CONCRETE_AND_ARRAYS,
NON_FINAL,
EVERYTHING;
private DefaultTyping() {
}
}
-
JAVA_LANG_OBJECT: 对象属性类型为Object时生效;
-
OBJECT_AND_NON_CONCRETE: 当对象属性类型为==Object或者非具体类型(抽象类和接口)==时生效;
-
NON_CONCRETE_AND_ARRAYS: 同上, 另外所有的数组元素的类型都是非具体类型或者对象类型;
-
NON_FINAL: 对所有非final类型或者非final类型元素的数组。
因此,当开启DefaultTyping后,会开发者在反序列化时指定要还原的类,过程中调用其构造方法,setter方法或某些特殊的getter方法,当这些方法中存在一些危险操作时就造成了代码执行。
反序列化漏洞
NON_FINAL,包含即将被序列化的类里的全部、非final的属性,就是相当于整个类、除final外的的属性信息都需要被序列化和反序列化。
enableDefaultTyping的NON_FINAL这个功能涉及到java著名的反序列化漏洞。各位系统之间调用数据的项目还是慎重使用.
影响范围
jackson-databind < 2.9.10.4
JDK < 6u201、7u191、8u182、11.0.1(LDAP)
@JsonUnwrapped
@Getter
@Setter
@ToString
public class Money {
private double remain;
}
@Getter
@Setter
@ToString
public class PersonInfo {
private String name;
private int id;
}
@Getter
@Setter
@ToString
public class Account {
private Money money;
private PersonInfo personInfo;
}
序列化之后的 json
{
"money": {
"remain": 1030.0
},
"personInfo": {
"name": "tangbaobao",
"id": 1
}
}
使用@JsonUnwrapped 来 扁平对象
@Getter
@Setter
@ToString
public class Account {
@JsonUnwrapped
private Money money;
@JsonUnwrapped
private PersonInfo personInfo;
}
结果
{
"remain": 1030.0,
"name": "tangbaobao",
"id": 1
}
@JsonIgnore
@JsonIgnore
private String fullName;
序列化和反序列化都忽略该属性
@JsonIgnoreProperties
@JsonIgnoreProperties(value = {"fullName", "comment"})
- 写在类上或者类的属性是个类(该属性上面)
- 序列化和反序列化都忽略==
get为true的时候说明字段允许序列化,反序列的时候忽略该字段
@JsonIgnoreProperties(value = {"usname","password"}, allowSetters = true)
set为true说明字段允许反序列化,序列化的时候忽略该字段
@JsonIgnoreProperties(value = {"usname","password"}, allowGetters = true)
@JsonProperty
- Using object field.
@JsonProperty("bookCategory")
private String category;
- Using Getter method.
@JsonProperty("bookCategory")
public String getCategory() {
return category;
}
- Using Setter method.
@JsonProperty("bookCategory")
public void setCategory(String category) {
this.category = category;
}
- value: 定义逻辑属性的名称
- access: 更改序列化和反序列化中逻辑属性的可见性
- defaultValue: 用于记录预期的默认值
- index: 定义与object指定的其他属性相关的属性的数字索引
- required: 定义在反序列化期间是否需要属性的值
// 只能序列化。所以反序列化,json对象变成对象的时候 对应的属性不会有值
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String name;
// 反序列化,json对象变成对象的时候,会将对应的属性赋值
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
Access.WRITE_ONLY:逻辑属性的可见性仅在我们将JSON数据设置为Java对象时即在反序列化时才可用
Access.READ_ONLY:逻辑属性的可见性仅在我们从Java对象获取JSON数据时才可用,即在序列化时
Access.READ_WRITE:逻辑属性的可见性在序列化和反序列化时都可用。
Access.AUTO:将自动确定逻辑属性的可见性,这是access元素的默认值。
枚举
public enum Gender {
@JsonProperty("male") GENDER_MALE,
@JsonProperty("female") GENDER_FEMALE;
}
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(Gender.GENDER_FEMALE)); // "female"
@JsonAlias
此注解的作用很大,用于反序列的时候,指定json别名,特别是在对接不同的api的时候,有的接入方的字段是sex,有的是xingbie,有的是gender但是又表示的同一个意思的时候,非常有用
但是在序列化时,即从Java对象获取JSON时,只使用实际的逻辑属性名而不是别名
class Person{
int age;
String name;
@JsonAlias({"xingbie","gender","sex"})
String sex;
}
测试代码如下
@Test
public void JsonAliasTest() throws Exception{
CombineJacksonAnnotation.Person person1 = om.readValue("{\n" +
" \"name\" : \"tom\",\n" +
" \"age\" : 12,\n" +
" \"sex\" : \"female\"\n" +
"}",CombineJacksonAnnotation.Person.class);
CombineJacksonAnnotation.Person person2 = om.readValue("{\n" +
" \"name\" : \"tom\",\n" +
" \"age\" : 12,\n" +
" \"xingbie\" : \"female\"\n" +
"}",CombineJacksonAnnotation.Person.class);
CombineJacksonAnnotation.Person person3 = om.readValue("{\n" +
" \"name\" : \"tom\",\n" +
" \"age\" : 12,\n" +
" \"gender\" : \"female\"\n" +
"}",CombineJacksonAnnotation.Person.class);
System.out.println(om.writeValueAsString(person1));
System.out.println(om.writeValueAsString(person2));
System.out.println(om.writeValueAsString(person3));
}
ut结果如下
{
"name" : "tom",
"age" : 12,
"sex" : "female"
}
{
"name" : "tom",
"age" : 12,
"sex" : "female"
}
{
"name" : "tom",
"age" : 12,
"sex" : "female"
}
@JsonFormat
@JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8")
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime gmtCreate;
前端和数据库都是
"gmtCreate": "2021-10-26 23:15:04"
如果出现
java.sql.SQLFeatureNotSupportedException: null
将druid的版本提升到1.2.6
即可
全局设置
/**
* 统一json输出风格
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (int i = 0; i < converters.size(); i++) {
if (converters.get(i) instanceof StringHttpMessageConverter) {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charsets.UTF_8);
stringHttpMessageConverter.setWriteAcceptCharset(false);
converters.set(i, stringHttpMessageConverter);
}
if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("JsonMapSerializer", Version.unknownVersion());
// 对LocalDateTime类,提供统一的序列化方式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
simpleModule.addSerializer(new LocalDateTimeSerializer(dateTimeFormatter));
objectMapper.registerModule(simpleModule);
// 统一返回数据的输出风格
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategy.SnakeCaseStrategy());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
converters.set(i, converter);
break;
}
}
}
java8 jackson
-
jackson-module-parameter-names
:此模块能够访问构造函数和方法参数的名称,从而允许省略@JsonProperty
-
jackson-datatype-jsr310
:支持Java8新增的JSR310时间API
-
jackson-datatype-jdk8
:除了Java8的时间API外其它的API的支持,如Optional
@JsonInclude
// 为null的属性不会被序列化
@JsonInclude(JsonInclude.Include.NON_NULL)
public static enum Include {
ALWAYS,
NON_NULL,
NON_ABSENT,
NON_EMPTY,
NON_DEFAULT,
CUSTOM,
USE_DEFAULTS;
private Include() {
}
}
@JsonDeserialize @JsonSerialize
@JsonDeserialize
- 是在反序列化时,所以就是对参数进行封装,故到的是 setXxxx() 方法,加到对应的 set 方法上
@JsonSerialize
- 是在反序列化时,所以需要获取数据,故到的是 getXxxx() 方法,加到对应的get方法上
使用场景一:
前端显示是万元,后端存的时候是元
@JsonSerialize(using = BudgetSerializer.class) // 对应get
@JsonDeserialize(using = BudgetDeserializer.class) // 对应set
private BigDecimal applyBudget;
@Slf4j
public class BudgetSerializer extends JsonSerializer<BigDecimal> {
@Override
public void serialize(BigDecimal s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
BigDecimal format = s;
if (format != null) {
// 元转万元
format = format.divide(new BigDecimal("10000"), 4, BigDecimal.ROUND_HALF_DOWN);
log.debug("元格式化万元:前 {}, 后 {}", s, format);
}
jsonGenerator.writeNumber(format);
}
}
后端需要元
@Slf4j
public class BudgetDeserializer extends JsonDeserializer<BigDecimal> {
@Override
public BigDecimal deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
try {
if (jsonParser == null || jsonParser.getText() == null) {
return null;
}
String s = jsonParser.getText();
BigDecimal format = new BigDecimal(StringUtils.isBlank(s) ? "0" : s);
// 万元转元
format = format.multiply(new BigDecimal("10000"));
log.debug("万元格式化元:前 {}, 后 {}", s, format);
return format;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
}
@JacksonInject
public class JacksonInjectTest implements Serializable {
private static final long serialVersionUID = -1401502282252657530L;
@JacksonInject(value = "id")
private Long id;
@JacksonInject
private String isHistory;
private String name;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getIsHistory() {
return this.isHistory;
}
public void setIsHistory(String isHistory) {
this.isHistory = isHistory;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "JacksonInjectTest{" +
"id=" + this.id +
", isHistory='" + this.isHistory + '\'' +
", name='" + this.name + '\'' +
'}';
}
}```
```java
@RestController
public class JacksonInjectController {
@GetMapping("/jacksonInjectTest")
public JacksonInjectTest jacksonInjectTest() throws IOException {
String s = "{\"name\": \"foo\"}";
InjectableValues.Std injectableValues = new InjectableValues.Std();
// 指定key为"defaultField1"对应的注入参数
injectableValues.addValue("id", 1L);
// 指定String类型对应的注入参数
injectableValues.addValue(String.class,"s");
ObjectMapper mapper = new ObjectMapper(); // 把注入参数的配置设置给mapper
mapper.setInjectableValues(injectableValues);
JacksonInjectTest jacksonInjectTest = mapper.readValue(s, JacksonInjectTest.class);
System.out.println(jacksonInjectTest);
return jacksonInjectTest;
// JacksonInjectTest{id=1, isHistory='s', name='foo'}
}
}
稍作修改
// useInput = OptBoolean.TRUE
@JacksonInject(value = "id", useInput = OptBoolean.FALSE)
private Long id;
String s = "{\"name\": \"foo\", \"id\": 2}";
InjectableValues.Std injectableValues = new InjectableValues.Std();
// 指定key为"defaultField1"对应的注入参数
injectableValues.addValue("id", 1L);
// 指定String类型对应的注入参数
injectableValues.addValue(String.class,"s");
ObjectMapper mapper = new ObjectMapper(); // 把注入参数的配置设置给mapper
mapper.setInjectableValues(injectableValues);
JacksonInjectTest jacksonInjectTest = mapper.readValue(s, JacksonInjectTest.class);
System.out.println(jacksonInjectTest);
// 无论是 OptBoolean.TRUE 还是OptBoolean.FALSE,json字符串中的id为2,反序列化都是2
// JacksonInjectTest{id=2, isHistory='s', name='foo'}
这个问题参考:https://stackoverflow.com/questions/55922600/how-to-use-jacksoninjectuseinput-optboolean-false
配置SerializerFeature
SerializationFeature.WRAP_ROOT_VALUE
是否环绕根元素,默认false,如果为true,则默认以类名作为根元素,你也可以通过@JsonRootName来自定义根元素名称
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE,true);
@JsonRootName("myPojo")
public static class TestPOJO{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
该类在序列化成json后类似如下:
{"myPojo":{"name":"aaaa"}}
SerializationFeature.INDENT_OUTPUT
缩放排列,全局配置不生效
objectMapper.configure(SerializationFeature.INDENT_OUTPUT,true);
{
"a" : "aaa",
"b" : "bbb",
"c" : "ccc",
"d" : "ddd"
}
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
序列化日期时以timestamps输出,默认true
比如如果一个类中有private Date date;这种日期属性,序列化后为:
{"date" : 1413800730456}
// 若不为true,则为
{"date" : "2014-10-20T10:26:06.604+0000"}
SerializationFeature.WRITE_ENUMS_USING_TO_STRING
序列化枚举是以toString()来输出,默认false,即默认以name()来输出
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
SerializationFeature.WRITE_ENUMS_USING_INDEX
序列化枚举是以ordinal()来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,true);
举例:
@Test
public void enumTest() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("myName");
testPOJO.setMyEnum(TestEnum.ENUM01);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,false);
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);
String jsonStr1 = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{\"myEnum\":\"ENUM01\",\"name\":\"myName\"}",jsonStr1);
ObjectMapper objectMapper2 = new ObjectMapper();
objectMapper2.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
String jsonStr2 = objectMapper2.writeValueAsString(testPOJO);
Assert.assertEquals("{\"myEnum\":\"enum_01\",\"name\":\"myName\"}",jsonStr2);
ObjectMapper objectMapper3 = new ObjectMapper();
objectMapper3.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,true);
String jsonStr3 = objectMapper3.writeValueAsString(testPOJO);
Assert.assertEquals("{\"myEnum\":0,\"name\":\"myName\"}",jsonStr3);
}
public static class TestPOJO{
TestPOJO(){}
private TestEnum myEnum;
private String name;
//getters、setters省略
}
public static enum TestEnum{
ENUM01("enum_01"),ENUM02("enum_01"),ENUM03("enum_01");
private String title;
TestEnum(String title) {
this.title = title;
}
@Override
public String toString() {
return title;
}
}
SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED
序列化单元素数组时不以数组来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,true);
@Test
public void singleElemArraysUnwrap() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("myName");
List<Integer> counts = new ArrayList<>();
counts.add(1);
testPOJO.setCounts(counts);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,false);
String jsonStr1 = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{\"name\":\"myName\",\"counts\":[1]}",jsonStr1);
ObjectMapper objectMapper2 = new ObjectMapper();
objectMapper2.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,true);
String jsonStr2 = objectMapper2.writeValueAsString(testPOJO);
Assert.assertEquals("{\"name\":\"myName\",\"counts\":1}",jsonStr2);
}
public static class TestPOJO{
private String name;
private List<Integer> counts;
//getters、setters省略
}
SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS
序列化Map时对key进行排序操作,默认false
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);
@Test
public void orderMapBykey() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("myName");
Map<String,Integer> counts = new HashMap<>();
counts.put("a",1);
counts.put("d",4);
counts.put("c",3);
counts.put("b",2);
counts.put("e",5);
testPOJO.setCounts(counts);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,false);
String jsonStr1 = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{\"name\":\"myName\",\"counts\":{\"d\":4,\"e\":5,\"b\":2,\"c\":3,\"a\":1}}",jsonStr1);
ObjectMapper objectMapper2 = new ObjectMapper();
objectMapper2.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);
String jsonStr2 = objectMapper2.writeValueAsString(testPOJO);
Assert.assertEquals("{\"name\":\"myName\",\"counts\":{\"a\":1,\"b\":2,\"c\":3,\"d\":4,\"e\":5}}",jsonStr2);
}
public static class TestPOJO{
private String name;
private Map<String,Integer> counts;
//getters、setters省略
}
SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS
序列化char[]时以json数组输出,默认false
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
@Test
public void charArraysAsJsonArrays() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("myName");
char[] counts = new char[]{'a','b','c','d'};
testPOJO.setCounts(counts);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,false);
String jsonStr1 = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{\"name\":\"myName\",\"counts\":\"abcd\"}",jsonStr1);
ObjectMapper objectMapper2 = new ObjectMapper();
objectMapper2.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
String jsonStr2 = objectMapper2.writeValueAsString(testPOJO);
Assert.assertEquals("{\"name\":\"myName\",\"counts\":[\"a\",\"b\",\"c\",\"d\"]}",jsonStr2);
}
public static class TestPOJO{
private String name;
private char[] counts;
//getters、setters省略
}
jsonStr1 {"name":"myName","counts":"abcd"}
jsonStr2 {"name":"myName","counts":["a","b","c","d"]}
SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN
序列化BigDecimal时之间输出原始数字还是科学计数,默认false,即是否以==toPlainString()==科学计数方式来输出
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
@Test
public void bigDecimalAsPlain() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("myName");
testPOJO.setCount(new BigDecimal("1e20"));
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,false);
String jsonStr1 = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{\"name\":\"myName\",\"count\":1E+20}",jsonStr1);
ObjectMapper objectMapper2 = new ObjectMapper();
objectMapper2.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,true);
String jsonStr2 = objectMapper2.writeValueAsString(testPOJO);
Assert.assertEquals("{\"name\":\"myName\",\"count\":100000000000000000000}",jsonStr2);
}
@JacksonAnnotation
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JacksonAnnotation
{
// for now, a pure tag annotation, no parameters
}}
从注释信息可以看出,此注解是其他所有jackson注解的元注解,打上了此注解的注解表明是jackson注解的一部分
@JacksonAnnotationsInside
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JacksonAnnotationsInside{
}
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JacksonAnnotation {
}
可以看到,JacksonAnnotation注解也打到了这个元注解上面,此注解也是一个元注解,一般用于将其他的注解一起打包成"组合"注解,虽然说jackson提供了很多的非常实用的注解给我们来用,但是产品的需求是无限的,很多时候,我们需要定义自己的注解,来满足我们的需求。
如下,我们定义了一个@CombineJacksonAnnotation注解,可以打在class上面,实现的功能是
- 序列化时,只有非
null
的属性
- 按照 name age sex 的顺序序列化属性,然后将此注解打在Person类上面,代码如下
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"name","age","sex"})
@JacksonAnnotationsInside
public @interface CombineJacksonAnnotation {
@Data
@AllArgsConstructor(staticName = "of")
@CombineJacksonAnnotation
class Person{
int age;
String name;
String sex;
}
}
public static ObjectMapper om = new ObjectMapper();
static {
// 缩放排列输出
om.enable(SerializationFeature.INDENT_OUTPUT);
}
@Test
public void JacksonAnnotationsInsideTest() throws Exception{ System.out.println(om.writeValueAsString(CombineJacksonAnnotation.Person.of(12,"tom",null)));
}
// 输出
{
"name" : "tom",
"age" : 12
}