jackson的使用

2023-11-11

文章目录

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() {
    }
}
  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

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

  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
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

jackson的使用 的相关文章

  • JavaFX:如何在 JavaFX 中正确使用 ProgressIndicator

    我是 JavaFX 新手 我的 JavaFX 应用程序有问题 我需要在数据库查询之前启动 ProgressIndicator 类型 INDETERMINATE 这是我的代码的一部分 spinner setVisible true passC
  • Google 分析“获取配置文件”不允许我选择我的帐户

    我已经根据最新的 v4 文档完成了一个有效的谷歌分析集成 我们需要去哪里获取配置文件 from 但面临以下问题 我的谷歌分析帐户上的两个不同的应用程序有两个不同的跟踪ID 对于第一个应用程序 我得到了谷歌服务 json我将文件放置在根目录中
  • 尽管使用了 NTP 偏移量,设备仍会在几秒后启动指令

    背景 我有两台物理设备 一台 Galaxy S3 手机 和一台 Asus 700T 平板电脑 我想在同一时间执行同一组指令 因此 我使用的是 Android 的平台框架 基本 SNTP 客户端代码 https github com andr
  • 同步 Set 线程的构造函数副本安全吗?

    获取同步版本的最简单方法java util Set将使用Collections synchronizedSet 像这样 Set mySyncSet Collections synchronizedSet new HashSet The Ja
  • Spring 3.1 Java配置和内部bean

    Bean public TimedRepository timedRepository RealRepository repo return new TimedRepository repo timer Adds some metrics
  • JavaFX TabPane 禁用按键切换选项卡

    我有一个Tab有一些内容 ScrollBar和别的 The ScrollBar has event handler for keys left and right 但如果我按下这些按钮Tabs被切换 因为TabPane还有一个密钥处理程序
  • 如何在 JavaFX 中设置滚动窗格的单位增量?

    The 滚动条 http docs oracle com javafx 2 api javafx scene control ScrollBar htmlJavaFX 中的类包含一个用于设置单位增量的属性 这就是我所追求的 但是我找不到如何
  • 仅在文件下载完成后设置 cookie。

    我有一个场景 我想告诉用户下载完成并提示关闭按钮 为此 我使用 jquery 插件来连续监视 cookie 以了解下载何时完成 我的问题是我想设置这个cookie fileDownload true and path 下载完成后立即进行 为
  • HttpURLConnection 中的 NTLM 身份验证在 JRE 中不起作用,但在 JDK 环境中起作用

    我正在使用 eclipse 开发应用程序的两个部分 Web 部件提供 REST 服务 并对服务的请求进行过滤waffle servlet NegotiateSecurityFilter它提取 Windows 登录信息来识别用户 客户端部分使
  • Kafka 0.10 Java 客户端超时异常:包含 1 条记录的批次已过期

    我有一个单节点 多 3 个代理 Zookeeper Kafka 设置 我正在使用 Kafka 0 10 Java 客户端 我编写了以下简单的远程 在与 Kafka 不同的服务器上 生产者 在代码中我用 MYIP 替换了我的公共 IP 地址
  • Java KeyListener:按下两个键时如何执行操作?

    请看下面的代码 import java awt event import javax swing import java awt public class KeyCheck extends JFrame private JButton ch
  • 为自定义 userdetailsservice 定义 bean

    我如何定义我的自定义UserDetailsServicebean 的方式使我的 spring mvc Web 应用程序能够使用我的底层 MySQL 数据库来检查用户和密码的身份验证 具体如下 我正在添加安全性spring petclinic
  • Java中对象类的继承

    当我读java书时 我遇到了 每个类都扩展类 Object 但是如果想要 B 类扩展 A 类 但是 B 类现在将具有多重继承 一个来自 Object 类 一个来自 A 类 如何解决冲突 谁能解释一下吗 它是多级继承 而不是多重 class
  • Java 8 Streams - 嵌套映射到列表

    firstlist stream map x gt return secondList stream map y gt return a string collect Collectors toList Output I need Get
  • 从多个文本文件读取数据[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我是Java编程新手 我正在尝试打印
  • 信号量如何工作?

    信号量可以小于0吗 我的意思是 假设我有一个 N 3 的信号量 并且我调用 down 4 次 那么 N 将保持为 0 但一个进程将被阻塞 反之亦然 如果一开始我调用 N 可以大于 3 吗 因为在我看来 如果 N 可以高于 3 如果一开始我调
  • 将字符串从代码页 1252 转换为 1250

    我怎样才能转换一个String将代码页 1252 中的字符解码为String在代码页 1250 中解码 例如 String str1252 String str1250 convert str1252 System out print st
  • 正确检查 FTP 服务器连接

    我在程序开始时打开与 FTP 服务器的连接 在对服务器执行操作之前 我想检查连接是否已成功建立 最简单快速的方式 因此如果连接消失 我将尝试再次连接 我用这段代码来做到这一点 private boolean checkConnection
  • 使用具有透明度的 IndexColorModel 绘制图像

    这是我的问题 我想在基于具有透明度的 IndexColorModel 的 BufferedImage 上应用转换 平移 旋转 剪辑 索引 0 是我的透明像素 索引 1 是黑色 索引 2 是白色 依此类推 源图像 即在转换之前 被实例化如下
  • 如何将react-native与php一起使用并获取返回数据始终为空

    我的回报始终为空 我似乎无法让它发挥作用 我如何将react native与php一起使用并获取json 任何人都可以帮忙吗 PHP myArray array myArray lat POST lat myArray lng POST l

随机推荐

  • CMake----if与option使用小记

    在CMake中if语法比较简单 if后面括号中的参数随着CMake版本的推进 在else和endif中也可以不用写了 if address else endif 对于if语法 比较常用的就是字符串比较了 这里个人简单用到过两种 一种是这个变
  • 使用scoped穿透方法实现修改vue中mint UI组件样式

    效果 代码 div class goods swiper div
  • 图书商城系统

    摘 要 随着信息科学技术的不断发展与完善 信息化已经成为个人之间甚至是国家之间商务发展的一大趋势 并且广泛应用于商业贸易 国际化的网络 计算机科学以及网络通信之中 电子商务正是依托信息化技术的迅猛发展将全球化的市场集中在网络平台之中 打破了
  • c++基本类型和变量

    基本类型 c 内置类型 setlocale LC ALL chs bool bo true char ch a wchar t wch L 中国 short sh 32767 32768 32767 int i 10 32768 32767
  • runas 显示740 所需的操作需要提升的解决方法

    域环境中 有些软件启动需要用到管理员权限 所以对user用户来说比较麻烦 对IT来说也挺麻烦 每次使用都需要输一次账号密码 后来使用了runas工具就方便了 虽然有些不安全 今天发现这个不起作用了 cmd里输入语句 提示 740 所需的操作
  • C语言内存四区的学习总结(一)---- 静态区

    最近重新学习C语言相关知识 重新提到内存四区的概念 那么在之前的学习的基础上 在这儿做一个简单的总结与分享 一 内存四区建立的流程 可以简单直观的查看下面的这个图片 直接的说明我们的程序在内存中是如何去存储 运行 程序运行的流程说明 1 操
  • 引入字体包

    我接触的设计师都比较喜欢用苹方字体 然后每次都要引入字体包 首先一定要设计师给ttf格式的文件 然后在scss中引入 font face font family PingFangSC Regular font weight normal s
  • pandas数据读取与清洗视频03-pd.read_csv()读取csv、txt文件

    本系列课程适用人群 python零基础数据分析的朋友 在校学生 职场中经常要处理各种数据表格 或大量数据 十万级以上 的朋友 喜欢图表可视化的朋友 系列视频目前可在B站观看 会定期更新 欢迎大家吐槽 本节概要 数据量较大时一般保存为csv或
  • Wireshark TS

    问题背景 用户反馈说观察到一个设备连接的奇怪问题 客户端 172 18 0 122 尝试连接到服务器 172 18 50 1 之后服务器回复 SYN ACK 再收到消息后不久 客户端直接发送 RST 并在一段时间后又重复尝试连接 总结下来就
  • RPC通信功能实现

    Table of Contents RPC通信功能实现 配置参数 调用方法 RPC通信功能实现 HBase的RPC通信功能主要基于Protobuf和NIO这两个组件来实现 在通信管道上选择的是protobuf对外声明的BlockingRpc
  • Linux——僵尸进程以及僵尸进程的处理

    僵尸进程 1 进程中的指令已经执行完成 但是进程PCB结构还没有回收 即子进程先于父进程退出后 子进程的PCB需要其父进程释放 但是父进程并没有释放子进程的PCB 这样的子进程就称为僵尸进程 2 父进程未结束 子进程结束 但父进程没有处理子
  • C语言三大标准C89,C99和C11

    C89 标准 1983 年美国国家标准局 American National Standards Institute 简称 ANSI 成立了一个委员会 专门来制定C语言标准 1989 年C语言标准被批准 被称为 ANSI X3 159 19
  • 59 KVM Skylark虚拟机混部-概述、架构及特性

    文章目录 59 KVM Skylark虚拟机混部 概述 架构及特性 59 1 Skylark概述 59 1 1 问题背景 59 1 2 总体介绍 59 2 架构及特性 59 2 1 总体实现框架 59 2 2 功耗干扰控制 59 2 3 L
  • 树-树的遍历(先序、中序、后序)

    树的遍历 树的遍历方式主要分为四种 先序 中序 后序和层序 在这篇博客中我将仔细介绍一下树的这四种遍历方式 先序遍历 先序遍历 也叫先根遍历 前序遍历 首先访问根结点然后遍历左子树 最后遍历右子树 在遍历左 右子树时 仍然先访问根结点 然后
  • discuz常用函数调用

    调用主题的查看次数 lang show G forum thread views discuz帖子正文下方有一组自动推荐的相关帖子 其调用代码为 div class mtw mbw h3 class pbm mbm bbda lang re
  • 【Bug修复】安装BurpSuite时,配置完Java环境后burp-loader-keygen.jar无法打开的问题

    前言 我们在安装BurpSuite时需要打开 jar文件 打开jar文件的前提是安装java环境 这里我安装的是1 8版本的 正常流程是安装好java环境可以直接打开 jar文件 而我的不行 我的图标 文件类型也没有任何变化 工具分享链接链
  • [Windows Azure] What is a cloud service?

    What is a cloud service When you create an application and run it in Windows Azure the code and configuration together a
  • 目标检测器训练过程总结(HyperLPR)

    1 项目背景 开源项目HyperLPR 在车牌粗定位 提取 阶段 依赖于训练好的模型文件cascade xml 而该文件是一个由基于OpenCV的Haar级联分类器训练出来的模型 HyperLPR作者有在他的博客中谈到了该模型文件的训练过程
  • 白盒测试的几种覆盖方法:语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、组合覆盖和路径覆盖详解

    文章转自 http www 51testing com html 44 n 3713444 html 白盒测试用例设计的一个很重要的评估标准就是对代码的覆盖度 一说到覆盖 大家都感觉非常熟悉 但是常见的覆盖都有哪些 各自有什么优缺点 在白盒
  • jackson的使用

    文章目录 json jackson databind ObjectMapper JSON字符串 gt 对象 JSON 字符输入流 gt 对象 JSON文件 gt 对象 JSON URL gt 对象 JSON字节输入流 gt 对象 JSON二