当当网网站建设需求分析,网站源码是什么意思,网站建设方案书 本案,app开发定制公司推荐Jackson介绍
Jackson是Java最受欢迎的JSON类库之一#xff0c;包含两个不同的解析器#xff1a;
Jackson ObjectMapper#xff0c;将JSON转化为Java对象#xff0c;或者转换为Jackson特定的树结构Jackson JsonParser#xff0c;JSON流解析器#xff0c;每次只解析一个J…Jackson介绍
Jackson是Java最受欢迎的JSON类库之一包含两个不同的解析器
Jackson ObjectMapper将JSON转化为Java对象或者转换为Jackson特定的树结构Jackson JsonParserJSON流解析器每次只解析一个JSON标记不做讲解
Jackson还包含两个不同的生成器
Jackson ObjectMapper可以从Java对象生成JSON或者是从Jackson的树结构生成JSONJackson Generator每次生成一个JSON标记不做讲解
在springMVC中的具体应用
在spring项目中如何实现自定义类型的转换例如如何将数字类型的属性转换为特定的枚举类型。一般来说项目都会使用不同的数字来代表属性的不同含义如果在代码中直接用数字类型表示可读性比较差因此都会定义一个枚举来表示。但是如果每次都需要手工将数字转换为枚举那就太麻烦了而且估计没人喜欢这样做。那解决方法是什么呢
那就是让Jackson来替我们完成这个工作。来看下如何做有如下代码
RestController
RequestMapping(/json/exam)
public class ExamController {PostMapping(/getExamList)public ResultListGetExamListResVo getExamList(Validated RequestBody GetExamListReqVo reqVo,AuthenticationPrincipal UserDetails userDetails)throws IOException {//......}
}GetExamListReqVo类
public class GetExamListReqVo {private ExamTypeEnum examType;// get set......
}ExamStatusEnum枚举类
public enum ExamTypeEnum implements EnumBase {UNKNOWN(0, ),EXAM_INDEPENDENT(1, 独立考试),EXAM_COURSE_SUITE(2, 关联课程);private final int code;private final String msg;ExamTypeEnum(int code, String msg) {this.code code;this.msg msg;}// get......
}/**
* 枚举类的公共接口所有枚举均实现了此接口
*/
public interface EnumBase {int getCode();String getMsg();static E extends Enum? EnumBase E codeOf(ClassE enumClass, Integer code) {E[] enumConstants enumClass.getEnumConstants();for (E e : enumConstants) {if (e.getCode() code) {return e;}}throw new IllegalArgumentException(the code didnt match any enum,code: code ,enum class: enumClass.getName());}
}目标将入参:{“examType”:1} 正确转换为 GetExamListReqVo 对象
为了完成这个目标只需要编写三个类
/*** 实现了EnumBase接口的枚举类对象序列化和反序列化*/
public class EnumBaseModule extends SimpleModule {public EnumBaseModule() {super(PackageVersion.VERSION);// 找到EnumBase接口所在的包下所有实现该接口的枚举类SetClass set ClassUtils.getAllClassesFromPackage(EnumBase.class.getPackage().getName()).stream().filter(clz - clz.isEnum() EnumBase.class.isAssignableFrom(clz)).collect(Collectors.toSet());// 动态注册所有实现了EnumBase接口枚举类的序列化器和反序列化器到Jacksonset.forEach(enumClass - {addDeserializer(enumClass, new EnumBaseDeserializer(enumClass));addSerializer(enumClass, new EnumBaseSerializer());});}
}/*** 用来序列化实现了EnumBase接口的枚举类*/
public class EnumBaseSerializer extends JsonSerializerEnumBase {Overridepublic void serialize(EnumBase value, JsonGenerator gen, SerializerProvider serializers)throws IOException {if (value ! null) {gen.writeNumber(value.getCode());} else {gen.writeNull();}}
}/*** 将前端传过来的数字转换为实现了EnumBase接口的枚举类对象*/
public class EnumBaseDeserializerE extends Enum? EnumBase extends JsonDeserializerEnumBase implements ContextualDeserializer {private ClassE targetClass;public EnumBaseDeserializer() {}public EnumBaseDeserializer(ClassE targetClass) {this.targetClass targetClass;}Overridepublic E deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {return EnumBase.codeOf(targetClass, p.getIntValue());}Overridepublic JsonDeserializer? createContextual(DeserializationContext ctxt,BeanProperty property) throws JsonMappingException {targetClass (ClassE) ctxt.getContextualType().getRawClass();return new EnumBaseDeserializer(targetClass);}
}然后配置一下springMVC
Configuration
public class WebMvcConfig implements WebMvcConfigurer {Overridepublic void configureMessageConverters(ListHttpMessageConverter? converters) {for (HttpMessageConverter? converter : converters) {if (converter instanceof StringHttpMessageConverter) {((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8);} else if (converter instanceof MappingJackson2HttpMessageConverter) {MappingJackson2HttpMessageConverter messageConverter (MappingJackson2HttpMessageConverter) converter;messageConverter.setObjectMapper(objectMapper());}}}Beanpublic ObjectMapper objectMapper() {ObjectMapper objectMapper new ObjectMapper();objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);// 注册所有实现了EnumBase接口的枚举类处理器objectMapper.registerModule(new EnumBaseModule());return objectMapper;}}ObjectMapper解析JSON的原理
默认情况下Jackson通过Java bean的getset方法通过去掉getset再把首字母小写得到的名字去和JSON的属性进行匹配。例如对于getBrand()和setBrand()经过处理得到brand,就会匹配到JSON的brand属性从而把JSON brand属性的值赋给bean的brand字段。通过一系列这样的处理就将JSON转换成了Java bean。如果需要以不同的方式来匹配那么就得使用定制的serializer和deserializer或者使用Jackson提供的众多的注解。
ObjectMapper创建Java对象的多种方式
Car类定义
public class Car {private String brand null;private Integer doors 0;// get set......
}测试代码: String carJson { \brand\ : \Mercedes\, \doors\ : 5 };// 从字符串创建Car car objectMapper.readValue(carJson, Car.class);System.out.println(objectMapper.writeValueAsString(car));// 从Reader创建Reader reader new StringReader(carJson);car objectMapper.readValue(reader, Car.class);System.out.println(objectMapper.writeValueAsString(car));File file ResourceUtils.getFile(classpath:car.json);// 从文件创建car objectMapper.readValue(file, Car.class);System.out.println(objectMapper.writeValueAsString(car));URL url ResourceUtils.getFile(classpath:car.json).toURI().toURL();// 从URL创建car objectMapper.readValue(url, Car.class);System.out.println(objectMapper.writeValueAsString(car));InputStream input new FileInputStream(file);// 从InputStream创建car objectMapper.readValue(input, Car.class);System.out.println(objectMapper.writeValueAsString(car));String jsonArray [{\brand\:\ford\}, {\brand\:\Fiat\}];// 解析为数组Car[] cars objectMapper.readValue(jsonArray, Car[].class);System.out.println(objectMapper.writeValueAsString(cars));// 解析为listListCar carList objectMapper.readValue(jsonArray, new TypeReferenceListCar() {});System.out.println(objectMapper.writeValueAsString(carList));String jsonObjectStr {\brand\:\ford\, \doors\:5};// 解析为MapMapString, Object map objectMapper.readValue(jsonObjectStr,new TypeReferenceMapString, Object() {});System.out.println(objectMapper.writeValueAsString(map));配置ObjectMapper的工作方式
有时JSON会拥有比Java对象更多的属性这种情况下Jackson默认会抛出异常大概意思是说JSON某个属性未知因为在Java bean中未找到该属性。
然而有时我们的确会遇到JSON属性比Java对象多的情况。例如我们从REST service获取的JSON会包含更多不需要的属性这种情况下Jackson允许通过配置来忽略未知的属性像这样
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);原始类型的null处理
public class Car {private String brand null;private int doors 0;// 原始类型int// get set......
}
对于{ “brand”:“Toyota”, “doors”:null }
Car类的doors是原始类型int不能被赋值为null因此Jackson对于原始类型的null值默认会忽略然而我们也可以配置Jackson在这种情况下做失败处理
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);设为true后对于试图解析原始类型的null就会抛出异常。还有其他更多的配置等等例如
// 遇到无效的子类型也不会解析失败
objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
// 对于空的bean也不会失败
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); 更多配置请查阅API说明
Jackson对于Date类的处理
默认情况下Date类型属性会被序列化long的毫秒数然而Jackson也支持将Date序列化成特定格式的日期字符串
public class Transaction {/*** 单独指定序列化后的格式和反序列化时以此格式来解析日期字符串*/JsonFormat(pattern yyyyMMdd HH:mm:ssSSS, locale zh_CN, timezone GMT8)private Date date null;private Date create null;// get set......
}
测试代码: Transaction transaction new Transaction();transaction.setCreate(new Date());transaction.setDate(new Date());String resJson objectMapper.writeValueAsString(transaction);// 默认把时间序列化为long// {date:20190618 11:33:42845,create:1560828822845}System.out.println(resJson);
可以改为序列化成日期字符串 SimpleDateFormat dateFormat new SimpleDateFormat(yyyy-MM-dd HH:mm:ss, Locale.SIMPLIFIED_CHINESE);dateFormat.setTimeZone(TimeZone.getTimeZone(GMT8));// 设置这个会影响所有Bean的Date序列化和反序列化objectMapper.setDateFormat(dateFormat);resJson objectMapper.writeValueAsString(transaction);// {date:20190618 11:33:42845,create:2019-06-18 11:33:42}System.out.println(resJson);String transactionJsonStr {\type\:\transfer\,\date\:\20190118 14:27:52052\,\create\:\2019-01-18 14:27:52\, \transactionTypeEnum\:\TICKET\,\transactionType\:2,\carTypeEnum\:3};transaction objectMapper.readValue(transactionJsonStr, Transaction.class);System.out.println(transaction);
Jackson的树模型JsonNode
Jackson内置有树模型用来代表JSON对象这个树模型具体有什么用呢当你不知道要解析的JSON的结构你就会发现它会很有用或者由于某种原因你没法创建一个类来表示这个JSON对象。另外一个好处是你可以在使用这个JSON对象之前操纵这个JSON对象核心类是JsonNode。 String carJson { \brand\ : \Mercedes\, \doors\ : 5, \owners\ : [\John\, \Jack\, \Jill\], \nestedObject\ : { \field\ : \value\ } };JsonNode jsonNode objectMapper.readValue(carJson, JsonNode.class);// 或者jsonNode objectMapper.readTree(carJson);// 取JSON属性值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);// 取JSON数组JsonNode jsonArray jsonNode.get(owners);JsonNode jsonArrayNode jsonArray.get(0);String john jsonArrayNode.asText();System.out.println(john john);// 取JSON内嵌对象JsonNode childNode jsonNode.get(nestedObject);JsonNode childField childNode.get(field);String field childField.asText();System.out.println(field field);
JsonNode类是不可变的意味着我们不能直接创建该类的对象然而可以使用JsonNode的子类ObjectNode来构建对象 // 创建ObjectNodeObjectNode objectNode objectMapper.createObjectNode();objectNode.put(brand, Mercedes);objectNode.put(doors, 5);ObjectNode nestNode objectMapper.createObjectNode();nestNode.put(field, value);objectNode.set(nestedObject, nestNode);System.out.println(objectMapper.writeValueAsString(objectNode));
Jackson注解
主要可分为三类
读写注解同时影响序列化和反序列化过程
JsonIgnoreJsonIgnorePropertiesJsonIgnoreTypeJsonAutoDetectJsonPropertyJsonTypeInfoJsonSubTypes
读注解只影响反序列化过程
JsonSetterJsonAnySetterJsonCreatorJsonDeserialize
写注解只影响序列化过程
JsonIncludeJsonGetterJsonAnyGetterJsonPropertyOrderJsonRawValueJsonValueJsonSerialize
JsonIgnore用于需要忽略的属性不参与序列化和反序列化
public class PersonIgnore {JsonIgnoreprivate long personId 0;private String name null;// get set......
}PersonIgnore obj new PersonIgnore(1,John);
// {name:John}
System.out.println(objectMapper.writeValueAsString(obj));
JsonIgnoreProperties用于需要忽略的一系列属性
JsonIgnoreProperties({firstName, lastName})
public class PersonIgnoreProperties {private long personId 0;private String firstName null;private String lastName null;// get set......
}PersonIgnoreProperties personIgnoreProperties new PersonIgnoreProperties(1,John,Rod);
// {personId:1}
System.out.println(objectMapper.writeValueAsString(personIgnoreProperties));;
JsonIgnoreType表明所有用到此类的地方都会被忽略
public class PersonIgnoreType {private long personId 0;private String name null;private Address address null;//get set......
}JsonIgnoreType
public class Address {private String streetName null;private String houseNumber null;private String zipCode null;private String city null;private String country null;// get set......
}PersonIgnoreType obj new PersonIgnoreType(1, John, new Address(China));
// {personId:1,name:John}
System.out.println(objectMapper.writeValueAsString(obj));
JsonAutoDetect用于检测需要将哪类访问权限的属性参与序列化和反序列化
JsonAutoDetect(fieldVisibility JsonAutoDetect.Visibility.ANY )
public class PersonAutoDetect {private long personId 123;private String name null;// get set......
}
JsonAutoDetect.Visibility用于指明可见性级别包括ANY, DEFAULT, NON_PRIVATE, NONE, PROTECTED_AND_PRIVATE and PUBLIC_ONLY
JsonProperty表明序列化和反序列化时以注解指明的为准
public class Person {JsonProperty(id) private long personId 0;private String name null;// get set......
}
JSON{ “id” : 1234, “name” : “John”}
JsonTypeInfo和JsonSubTypes用来处理多态类型的序列化及反序列化这样说有点抽象来看下具体例子
抽象父类:
JsonTypeInfo(use JsonTypeInfo.Id.CLASS)
JsonSubTypes({JsonSubTypes.Type(value MemberInformation.class),JsonSubTypes.Type(value EcardUserInformation.class)})
JsonIgnoreProperties(ignoreUnknown true)
public abstract class LoginUserInformation implements Serializable {private static final long serialVersionUID -933578885036345619L;private String userType 1;private String ip 127.0.0.1;// get set......
}
两个具体子类:
public class MemberInformation extends LoginUserInformation implements Serializable {private static final long serialVersionUID 3334937746962893477L;private String fpcardno 513712340023;private String passwd 12346;// get set......
}public class EcardUserInformation extends LoginUserInformation implements Serializable {private static final long serialVersionUID -8191715041208933113L;private String aid;private String cellPhone;private String email;// get set......
}
测试代码: LoginUserInformation loginUserInformation new EcardUserInformation();((EcardUserInformation) loginUserInformation).setAid(124124124);// {class:org.javamaster.b2c.test.model.jackson.EcardUserInformation,userType:1,ip:127.0.0.1,// aid:124124124,cellPhone:null,email:null}System.out.println(objectMapper.writeValueAsString(loginUserInformation));LoginUserInformation memberInformation new MemberInformation();((MemberInformation) memberInformation).setPasswd(qq123123);// {class:org.javamaster.b2c.test.model.jackson.MemberInformation,userType:1,ip:127.0.0.1,// fpcardno:513712340023,passwd:qq123123}System.out.println(objectMapper.writeValueAsString(memberInformation));String jsonStr {\class\:\org.javamaster.b2c.core.model.jackson.MemberInformation\,\userType\:\1\, \ip\:\127.0.0.1\,\fpcardno\:\513712340023\,\passwd\:\qq123123\};memberInformation objectMapper.readValue(jsonStr, LoginUserInformation.class);System.out.println(objectMapper.writeValueAsString(memberInformation));
JsonSetter用于表明set方法应该匹配的JSON的属性当Java bean的属性名和JSON的属性名不一致时就可以使用此注解
public class Person {private long personId 0;private String name null;JsonSetter(id) // 应该和JSON的id属性匹配public void setPersonId(long personId) { this.personId personId; }// get set......
}
JSON{ “id” : 1234, “name” : “John”}
JsonAnySetter用于指明将所有JSON未知的属性都收集在一起
public class Bag {private MapString, Object properties new HashMap();JsonAnySetterpublic void set(String fieldName, Object value){this.properties.put(fieldName, value);}public Object get(String fieldName){return this.properties.get(fieldName);}
}
所有JSON未知的属性都会收集到Bag类的properties属性里
JsonCreator用于表明Java bean有一个构造方法能够匹配JSON的属性和bean的属性对于一些没有set方法的bean不可变对象来说这个注解是很有用的。
public class PersonImmutable {private final long id;private final String name;JsonCreatorpublic PersonImmutable(JsonProperty(id)long id, JsonProperty(name)String name) {this.id id;this.name name;}// get set......
}
JSON{ “id” : 1234, “name” : “John”}
JsonDeserialize用于定制bean属性的反序列化过程
public class PersonDeserialize {private long id 0;private String name null;// 这里我们想把1映射为true0映射为falseJsonDeserialize(using OptimizedBooleanDeserializer.class)private boolean enabled false;// get set......
}public class OptimizedBooleanDeserializer extends JsonDeserializerBoolean {Overridepublic Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {String text jsonParser.getText();if (0.equals(text)) {return false;} else {return true;}}
}
JSON{“id” : 1234, “name” : “John”“enabled”: 0}
JsonInclude用于表明值满足特定条件的属性才会被序列化 // 说明值不为null且不为空字符串的属性才会被序列化
JsonInclude(JsonInclude.Include.NON_EMPTY)
public class PersonInclude {private long personId 0;private String name null;private String name1 ;private String name2 John;// get set......
}
JSON{“personId” : 0, “name2” : “John”}
JsonGetter用于指明序列化后的属性名而不是用bean的字段名
public class PersonGetter {private long personId 0;JsonGetter(id)public long personId() { return this.personId; }JsonSetter(id)public void personId(long personId) { this.personId personId; }
}
序列化后 {“id”: 0}
JsonAnyGetter能够让你使用一个Map作为容器包含任何你想要序列化的属性
public class PersonAnyGetter {private MapString, Object properties new HashMap();JsonAnyGetterpublic MapString, Object properties() {return properties;}public MapString, Object getProperties() {return properties;}
}
JsonPropertyOrder用于指明bean属性序列化的顺序
JsonPropertyOrder({name, personId})
public class PersonPropertyOrder {private long personId 0;private String name John;// get set......
}
序列化后 {“name”:“John”,“personId”,0}
JsonRawValue用于表明属性值不做任何处理原样输出String类型的值序列化后会被双引号括住使用此注解后将不会加上双引号
public class PersonRawValue {private long personId 0;JsonRawValueprivate String address $#;// get set......
}
不加此注解时输出是这样的{“personId”:0,“address”:KaTeX parse error: Expected EOF, got # at position 1: #̲}加了后输出是这样的{…#}这很明显是非法的JSON结构所以我们为什么要这么做呢这么做的理由是address属性的值是JSON字符串来的像这样
public class PersonRawValue {private long personId 0;JsonRawValueprivate String address { \street\ : \Wall Street\, \no\:1};// get set......
}
序列化后是这样的{“personId”:0,“address”:{ “street” : “Wall Street”, “no”:1}}
不加注解时序列化后是这样的{“personId”:0,“address”:“{ “street” : “Wall Street”, “no”:1}”}
JsonValue表明Jackson不做序列化由此注解标注的方法完成序列化工作
public class PersonValue {private long personId 0;private String name null;JsonValuepublic String toJson(){return this.personId , this.name;}
}序列化后是这样的“0,null”
JsonSerialize用于定制bean属性的序列化过程
public class PersonSerializer {private long personId 0;private String name John;// 这里希望把false序列化成0true序列化成1JsonSerialize(using OptimizedBooleanSerializer.class)private boolean enabled false;// get set......
}public class OptimizedBooleanSerializer extends JsonSerializerBoolean {Overridepublic void serialize(Boolean aBoolean, JsonGenerator jsonGenerator,SerializerProvider serializerProvider)throws IOException {if (aBoolean) {jsonGenerator.writeNumber(1);} else {jsonGenerator.writeNumber(0);}}
}
完全定制类的序列化和反序列化过程 // 完全定制类的序列化和反序列化过程SimpleModule carModule new CarModule();// 注册针对这个类型的处理模块objectMapper.registerModule(carModule);Car car new Car();car.setBrand(BMW);car.setDoors(4);String json objectMapper.writeValueAsString(car);System.out.println(json);car objectMapper.readValue(json, Car.class);System.out.println(objectMapper.writeValueAsString(car));
其中CarModule CarSerializer CarDeserializer的定义
// 类型处理模块
public class CarModule extends SimpleModule {public CarModule() {super(PackageVersion.VERSION);addDeserializer(Car.class, new CarDeserializer(Car.class));addSerializer(Car.class, new CarSerializer(Car.class));}
}// 序列化器
public class CarSerializer extends StdSerializerCar {private static final long serialVersionUID 2807109332342106505L;public CarSerializer(ClassCar c) {super(c);}Overridepublic void serialize(Car car, JsonGenerator jsonGenerator,SerializerProvider serializerProvider)throws IOException {jsonGenerator.writeStartObject();if (car.getBrand() ! null) {jsonGenerator.writeStringField(brand, car.getBrand());} else {jsonGenerator.writeNullField(brand);}if (car.getDoors() ! null) {jsonGenerator.writeNumberField(doors, car.getDoors());} else {jsonGenerator.writeNullField(doors);}jsonGenerator.writeEndObject();}
}// 反序列化器
public class CarDeserializer extends StdDeserializerCar {private static final long serialVersionUID 4977601024588834191L;public CarDeserializer(Class? c) {super(c);}Overridepublic Car deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException {Car car new Car();while (!parser.isClosed()) {JsonToken jsonToken parser.nextToken();if (JsonToken.FIELD_NAME jsonToken) {String fieldName parser.getCurrentName();parser.nextToken();if (doors.equals(fieldName)) {car.setDoors(parser.getValueAsInt());} else if (brand.equals(fieldName)) {car.setBrand(parser.getValueAsString());}}}return car;}
}