jackson-databind的使用

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


<dependency> 
    <groupId>com.fasterxml.jackson.coregroupId> 
        <artifactId>jackson-databindartifactId> 
    <version>2.5.3version>
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() {
    }
}
  1. JAVA_LANG_OBJECT: 对象属性类型为Object时生效;

  2. OBJECT_AND_NON_CONCRETE: 当对象属性类型为==Object或者非具体类型(抽象类和接口)==时生效;

  3. NON_CONCRETE_AND_ARRAYS: 同上, 另外所有的数组元素的类型都是非具体类型或者对象类型;

  4. 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

  1. Using object field.
@JsonProperty("bookCategory")	
private String category; 
  1. Using Getter method.
@JsonProperty("bookCategory")	
public String getCategory() {
   return category;
} 
  1. 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

json变成对象时,即反序列化时,json缺少了一些实体类的属性,此时反序列化该属性会变成null,若不想这样,想要有默认值的时候,则可以使用@JacksonInject

//value 相当于id,之后会用到,
//userInput为FALSE的时候是属性值不覆盖默认值,相当于定死了这个字段的值。默认为true是会覆盖
//@JacksonInject("defaultUsername")如果我们就是为了值为空的时候注入默认的值,这么写就行了
@JacksonInject(value= "defaultUsername", useInput= OptBoolean.FALSE)
private String username; 

配置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上面,实现的功能是

  1. 序列化时,只有非null的属性
  2. 按照 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
}

你可能感兴趣的:(@JsonUnwrapped,java,json)