Jackson笔记

时间:2022-07-22
本文章向大家介绍Jackson笔记,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

简介

Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson 是最流行的 json 解析器之一 。 Spring MVC 的默认 json 解析器便是 Jackson。 Jackson 优点很多。 Jackson 所依赖的 jar 包较少 ,简单易用。与其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比较快;Jackson 运行时占用内存比较低,性能比较好;Jackson 有灵活的 API,可以很容易进行扩展和定制。

Jackson 的 1.x 版本的包名是 org.codehaus.jackson ,当升级到 2.x 版本时,包名变为 com.fasterxml.jackson,本文讨论的内容是基于最新的 Jackson 的 2.10.1 版本。

Jackson 的核心模块由三部分组成

  • jackson-core:核心包,提供基于“流模式”解析的相关 API,它包括 JsonPaser 和 JsonGenerator。 Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。
  • jackson-annotations:注解包,提供标准注解功能;
  • jackson-databind:数据绑定包,提供基于“对象绑定”解析的相关 API( ObjectMapper )和“树模型”解析的相关 API(JsonNode);基于“对象绑定”解析的 API 和“树模型”解析的 API 依赖基于“流模式”解析的 API。

导入依赖

------------------------  start ------------------------

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.1</version>
</dependency>
		
------------------------  end ------------------------
		
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
    <scope>provided</scope>
</dependency>

jackson-databind 依赖 jackson-corejackson-annotations,当添加 jackson-databind 之后, jackson-corejackson-annotations 也随之添加到 Java 项目工程中。在添加相关依赖包之后,就可以使用 Jackson。

Jackson ObjectMapper

Jackson 最常用的 API 就是基于“对象绑定”的 ObjectMapper。下面是一个 ObjectMapper 的使用的简单示例。

简单示例

这个例子中涉及三个类 Person、Address、PhoneNumber

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PhoneNumber {

    private String code;
    private String number;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {

    private String city;
    private String street;
}
@Data
public class Person {

    private String firstName;
    private String lastName;
    private Address address;
    private List<PhoneNumber> phoneNumberList;
}
/**
 * 入门示例
 */
@Test
void objectMapper() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 将对象转成 json
    String objectToJson = objectMapper.writeValueAsString(newPerson());
    System.out.println(objectToJson);

    // 将 json 转成对象
    Person person = objectMapper.readValue(objectToJson, Person.class);
    System.out.println(person);
}


private static Person newPerson() {
    List<PhoneNumber> phoneNumberList = new ArrayList<>();

    phoneNumberList.add(new PhoneNumber("86", "0411-12345678"));
    phoneNumberList.add(new PhoneNumber("86", "12312412323"));

    Address address = new Address("Foshan", "Chancheng");

    Person person = new Person();
    person.setFirstName("Chen");
    person.setLastName("Ray");
    person.setAddress(address);
    person.setPhoneNumberList(phoneNumberList);

    return person;
}

ObjectMapper 通过 writeValue 系列方法将 java 对象序列化为 json,并将 json 存储成不同的格式。

ObjectMapper 通过 readValue 系列方法从不同的数据源像将 json 反序列化为 java 对象。

执行结果

{"firstName":"Chen","lastName":"Ray","phoneNumberList":[{"code":"86","number":"0411-12345678"},{"code":"86","number":"12312412323"}]}

Person(firstName=Chen, lastName=Ray, address=null, phoneNumberList=[PhoneNumber(code=86, number=0411-12345678), PhoneNumber(code=86, number=12312412323)])

Jackson 读写 JSON

writeValue

ObjectMapper 也提供了下面的 writeValue 方法,帮助我们很方便的将对象输出到不同的目的地。

writeValue(File resultFile, Object value)
writeValue(OutputStream out, Object value)
writeValue(DataOutput out, Object value)
writeValue(Writer w, Object value)
writeValueAsString(Object value)
writeValueAsBytes(Object value)

readValue

ObjectMapper 提供了下面的 readValue 方法,帮助我们很方便的从不同的数据源读取对象。

readValue(File src, Class<T> valueType)
readValue(URL src, Class<T> valueType)
readValue(String content, Class<T> valueType)
readValue(Reader src, Class<T> valueType)
readValue(InputStream src, Class<T> valueType)
readValue(byte[] src, Class<T> valueType)
readValue(DataInput src, Class<T> valueType)

读取示例

我们还可以读取 JSON 到数组(Array),列表(List)和映射(Map)中,下面是三个简单的例子。

数组(Array)

/**
 * 读取 JSON 到数组(Array)
 */
@Test
void readJsonToArray() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 准备的 json
    String json = "[{"firstName":"Bo","lastName":"Shang"}, {"firstName":"San","lastName":"Zhang"}]";

    // 将 json 转成数组
    Person[] people = objectMapper.readValue(json, Person[].class);
    for (Person p :
            people) {
        System.out.println(p);
    }
}

列表(List)

/**
 * 读取 JSON 到列表(List)
 */
@Test
void readJsonToList() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 准备的 json
    String json = "[{"firstName":"Bo","lastName":"Shang"}, {"firstName":"San","lastName":"Zhang"}]";

    // 将 json 转成列表
    List<Person> people = objectMapper.readValue(json, new TypeReference<List<Person>>() {});
    for (Person p :
            people) {
        System.out.println(p);
    }
}

映射(Map)

/**
 * 读取 JSON 到映射(Map)
 */
@Test
void readJsonToMap() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 准备的 json
    String json = "{"firstName":"Bo","lastName":"Shang"}";

    // 将 json 转成映射
    Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
    System.out.println(map);
}

Jackson 注解 - 忽略字段

如果在读写 JSON 时,我们想忽略某些字段,我们可以使用下面的注解。

@JsonIgnore

这个注解是用在字段上,get或者set方法上,效果都是一样的,用来在实体类序列化和反序列化的时候忽略该字段字段。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    @JsonIgnore
    private String username;
    private String password;
    private Integer age;
}

测试接口

@RestController
public class UserController {

    @RequestMapping("/test")
    public User test(@RequestBody User u) {
        if (u != null) {
            return u;
        }
        User user = new User();
        user.setUsername("Ray");
        return user;
    }
}

访问这个接口返回的参数就不包含username这个字段了

{
  "password": null,
  "age": null
}

然而我们访问这个接口带过去的参数username这个字段也不会反序列化

{
   "username" : "小明",
   "password" : "123",
   "age" : 15
}

反序列化username属性为空,结果如下:

{
    "password": "123",
    "age": 15
}

由此可见当我们需要在序列化和反序列化的时候忽略某个字段的时候就用这个注解加在字段上面就行了。

@JsonIgnoreProperties

这个注解跟 @JsonIgnore 的效果是一样的,区别就是这个注解是用在类上面的,在需要的注解比较多的情况下,用来一次性定义忽略的字段。

@JsonIgnoreProperties({"username","password"})
public class User {
    private String username;
    private String password;
    private Integer age;
}

这样子useranme和password这两个字段在序列化和反序列化的时候就都忽略了。

{
  "age": null
}

allowGetters

还有一个用法就是配合allowGetters,allowSetters一起用用来控制字段忽视是在序列化还是反序列化,这样更加灵活,如下所示

//allowGetters为true的时候说明字段允许序列化,反序列的时候忽略该字段
@JsonIgnoreProperties(value = {"usname","password"},allowGetters = true)
public class User {
    private String username;
    private String password;
    private Integer age;
    }
//allowSetters为true说明字段允许反序列化,序列化的时候忽略该字段
@JsonIgnoreProperties(value = {"usname","password"},allowSetters = true)
public class User {
    private String username;
    private String password;
    private Integer age;

ignoreUnknown

这样一个类,如果 json 字符串中的字段数量与类的字段不匹配,这里多了一个 first,如:

// 准备的 json
String json = "[{"firstName":"Bo","first":"B","lastName":"Shang"}, {"firstName":"San","lastName":"Zhang"}]";

准备的实体类,缺少 first 属性

@Data
public class Person {

    private String firstName;
    private String lastName;
    private Address address;
    private List<PhoneNumber> phoneNumberList;

    private Date birthday;
}

此时就会出现UnrecognizedPropertyException异常,大致意思是未识别到 first 属性,没有匹配到忽略的标记:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "first" (class io.ray.jackson.entity.Person), not marked as ignorable (4 known properties: "lastName", "firstName", "phoneNumberList", "birthday"])
 at [Source: (String)"[{"firstName":"Bo","first":"B","lastName":"Shang"}, {"firstName":"San","lastName":"Zhang"}]"; line: 1, column: 29] (through reference chain: java.lang.Object[][0]->io.ray.jackson.entity.Person["first"])

给 Person 类增加注解,忽略未知错误:

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {

    private String firstName;
    private String lastName;
    private Address address;
    private List<PhoneNumber> phoneNumberList;

    private Date birthday;
}

问题就解决了。

@JsonIgnoreType

这个注解是用在类上面的表明这个类在序列化和反序列化的时候被忽略,此时的 Address 为 null。

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreType
public class Address {

    private String city;
    private String street;

}

Jackson 注解 - 忽略空字段

前端的同事要求说尽量不要有null,可有为空串“” 或者 0 或者 [], 但尽量不要null。 所以@JsonInclude(Include.NON_NULL) 这个注解放在类头上就可以解决。 实体类与json互转的时候 属性值为null的不参与序列化。

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class User {

    private String username;
    private String password;
    private Integer age;
}

测试示例

/**
 * 忽略空字段
 */
@Test
void nonEmpty() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 将对象转成 json
    String json = objectMapper.writeValueAsString(newUser());
    System.out.println(json);
}

private static User newUser() {
    User user = new User();
    user.setUsername("Ray");
    user.setAge(18);
    //user.setPassword("123456");

    return user;
}

执行结果

{"username":"Ray","password":null,"age":18}

-------------- 使用前后对比 --------------

{"username":"Ray","age":18}

Jackson 注解 - 修改字段名

如果 JSON 和对象字段名不匹配,我们可以使用注解 @JsonGetter 和 @JsonSetter

实体类,注解用于 getter 和 setter 方法上。

@ToString
public class PersonTwo {

    private String firstName;
    private String lastName;

    private Date birthday;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @JsonGetter("age")
    public Date getBirthday() {
        return birthday;
    }

    @JsonSetter("age")
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

测试示例

/**
 * 修改字段名
 */
@Test
void updateFiled() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 将对象转成 json
    String json = objectMapper.writeValueAsString(newPersonTwo());
    System.out.println(json);

    // 将 json 转成对象
    PersonTwo personTwo = objectMapper.readValue(json, PersonTwo.class);
    System.out.println(personTwo);
}

private static PersonTwo newPersonTwo() {
    PersonTwo personTwo = new PersonTwo();
    personTwo.setFirstName("Chen");
    personTwo.setLastName("Ray");
    personTwo.setBirthday(new Date());

    return personTwo;
}

执行结果

// birthday --> age
{"firstName":"Chen","lastName":"Ray","age":1582447798458}

// toString
PersonTwo(firstName=Chen, lastName=Ray, birthday=Sun Feb 23 16:49:58 CST 2020)

Jackson 注解 - 指定输出顺序

默认情况下,字段的输出顺序和它们在类中的位置一致,我们也可以使用注解 @JsonPropertyOrder 自己指定顺序。

@ToString
@Data
@JsonPropertyOrder({"lastName", "birthday", "firstName"})
public class PersonTwo {

    private String firstName;
    private String lastName;

    private Date birthday;

}

执行结果

// 指定输出顺序
{"lastName":"Ray","birthday":1582449375241,"firstName":"Chen"}

// toString
PersonTwo(firstName=Chen, lastName=Ray, birthday=Sun Feb 23 17:16:15 CST 2020)

Jackson 注解 - 输出 JSON 字段

如果一个对象中某个字段中的值是 JSON,输出整个对象会有问题,这时我们可以使用注解 @JsonRawValue,无转换的将属性值写入到json 字符串中。

@Data
public class PersonThree {

    private String firstName;
    private String lastName;

    @JsonRawValue
    private String address;
}
/**
 * 输出 JSON 字段
 */
@Test
void outputFiled() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 将对象转成 json
    String json = objectMapper.writeValueAsString(newPersonThree());
    System.out.println(json);
}

private static PersonThree newPersonThree() {
    PersonThree personThree = new PersonThree();
    personThree.setFirstName("Chen");
    personThree.setLastName("Ray");
    personThree.setAddress("{"country":"China", "city":"Gd"}");

    return personThree;
}

执行结果

{"firstName":"Chen","lastName":"Ray","address":{"country":"China", "city":"Gd"}}

Jackson 注解 - 自定义输出格式

如果你想自定义输出的格式,我们可以使用注解 @JsonValue

public class PersonFour {

    private String firstName;
    private String lastName;

    @JsonValue
    @Override
    public String toString() {
        return "PersonFour{" +
                "firstName='" + firstName + ''' +
                ", lastName='" + lastName + ''' +
                '}';
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}
/**
 * 自定义输出格式
 */
@Test
void jsonValue() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 将对象转成 json
    String json = objectMapper.writeValueAsString(newPersonFour());
    System.out.println(json);
}

private static PersonFour newPersonFour() {
    PersonFour personFour = new PersonFour();
    personFour.setFirstName("Chen");
    personFour.setLastName("Ray");

    return personFour;
}

执行结果

{"firstName":"Chen","lastName":"Ray"}

-------------- 使用前后对比 --------------

// toString 格式
"PersonFour{firstName='Chen', lastName='Ray'}"

Jackson 注解 - 使用构造器

如果你的类没有 setter 方法,我们也可以使用注解 @JsonCreator 修饰构造器,使用 @JsonProperty 修饰构造器属性。

@ToString
public class PersonFive {

    private String firstName;
    private String lastName;

    @JsonCreator
    public PersonFive(@JsonProperty("firstName") String firstName,
                      @JsonProperty("lastName") String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }
}
/**
 * 使用构造器
 */
@Test
void userCreator() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // json 消息
    String json = "{"firstName":"Chen","lastName":"Ray"}";

    // 将 json 转成对象
    PersonFive personFive = objectMapper.readValue(json, PersonFive.class);
    System.out.println(personFive);
}

执行结果

PersonFive(firstName=Chen, lastName=Ray)

Jackson 注解 - 使用 Map存储JSON

我们也可以使用 Map 存储 JSON, 而不用创建对应的对象。

@ToString
public class PersonMap {

    private Map<String, Object> properties = new HashMap<>();

    @JsonAnySetter
    public void set(String fieldName, Object val) {
        this.properties.put(fieldName, val);
    }

    @JsonAnyGetter
    public Object get(String fieldName) {
        return this.properties.get(fieldName);
    }
}
/**
 * 使用 Map存储JSON
 */
@Test
void setMap() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // json 消息
    String json = "{"firstName":"Chen","lastName":"Ray"}";

    // 将 json 转成对象
    PersonMap personMap = objectMapper.readValue(json, PersonMap.class);
    System.out.println(personMap);
}

执行结果

PersonMap(properties={firstName=Chen, lastName=Ray})

Jackson 注解 - 类型转换

如果输入输出 JSON 时需要类型转换,我们可以使用注解 @JsonDeserialize@JsonSerialize

序列化类

public class BooleanToIntSerializer extends JsonSerializer<Boolean> {

    @Override
    public void serialize(Boolean aBoolean, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (aBoolean) {
            jsonGenerator.writeNumber(1);
        } else {
            jsonGenerator.writeNumber(0);
        }
    }
}

反序列化类

public class BooleanToIntDeserializer extends JsonDeserializer<Boolean> {

    @Override
    public Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String val = jsonParser.getText();
        if ("0".equals(val)) {
            return false;
        }
        return true;
    }
}
@Data
public class PersonSix {

    private String firstName;
    private String lastName;

    @JsonDeserialize(using = BooleanToIntDeserializer.class)
    @JsonSerialize(using = BooleanToIntSerializer.class)
    private Boolean isMale;
}
/**
 * 类型转换
 */
@Test
void jsonConvert() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 将对象转成 json
    String json = objectMapper.writeValueAsString(newPersonSix());
    System.out.println(json);

    // 将 json 转成对象
    PersonSix personSix = objectMapper.readValue(json, PersonSix.class);
    System.out.println(personSix);
}

private static PersonSix newPersonSix() {
    PersonSix personSix = new PersonSix();
    personSix.setFirstName("Chen");
    personSix.setLastName("Ray");
    personSix.setIsMale(true);

    return personSix;
}

执行结果

// 序列化
{"firstName":"Chen","lastName":"Ray","isMale":1}

-------------- 类型转换对比 --------------

// 反序列化
PersonSix(firstName=Chen, lastName=Ray, isMale=true)

Jackson 注解 - 输出私有字段

默认情况下,没有 setter 和 getter 方法的字段不会被输出,我们也可以使用注解 @JsonAutoDetect 设置可见性。

@ToString
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class PersonSeven {

    private Integer id = 0;

    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}
/**
 * 输出私有字段
 */
@Test
void outputPrivate() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 将对象转成 json
    String json = objectMapper.writeValueAsString(newPersonSeven());
    System.out.println(json);
}

private static PersonSeven newPersonSeven() {
    PersonSeven personSeven = new PersonSeven();
    personSeven.setFirstName("Chen");
    personSeven.setLastName("Ray");

    return personSeven;
}

执行结果

{"id":0,"firstName":"Chen","lastName":"Ray"}

Jackson JsonNode

除了 ObjectMapper 外,如果你不想创建和消息格式一样的对象模型,我们还可以使用 JsonNode 来访问 JSON 消息,下面是一个简单的例子

@Test
void jsonNode() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // json 消息
    String json = "{"firstName":"Chen","lastName":"Ray"}";

    // 将 json 转成 JsonNode 对象
    JsonNode rootNode = objectMapper.readTree(json);
		
    // 或者这样 转成 JsonNode 对象
    // JsonNode rootNode = objectMapper.readValue(json, JsonNode.class);

    // 得到节点值
    JsonNode firstNameNode = rootNode.get("firstName");
    System.out.println("firstNameNode = " + firstNameNode.asText());

    JsonNode lastNameNode = rootNode.get("lastName");
    System.out.println("lastNameNode = " + lastNameNode.asText());

    // 创建新节点
    ObjectNode newNode = objectMapper.createObjectNode();
    newNode.setAll((ObjectNode)rootNode);
    newNode.put("age", 18);

    // 将 JsonNode 对象转成 json
    String newJson = objectMapper.writeValueAsString(newNode);
    System.out.println(newJson);
}

JSON字符串被解析为JsonNode对象而不是 Person 对象,只需将 JsonNode.class 第二个参数传递给readValue()方法而不是 Person.class 以下是JsonNode使用该 readValue() 方法将JSON解析的示例:

JsonNode rootNode = objectMapper.readValue(json, JsonNode.class);

该ObjectMapper中还有一个特殊的 readTree(),它总是返回一个方法 JsonNode。以下是JsonNode使用该 ObjectMapper.readTree() 方法将JSON解析的示例:

JsonNode rootNode = objectMapper.readTree(json);

执行结果

firstNameNode = Chen
lastNameNode = Ray
{"firstName":"Chen","lastName":"Ray","age":18}

Jackson JsonNode 与 Object 互转

/**
 * Object 与 JsonNode 互转
 */
@Test
void objectAndJsonNode() throws JsonProcessingException {
    // 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // json 消息
    String json = "{"firstName":"Chen","lastName":"Ray"}";

    // 创建 JsonNode 对象
    JsonNode jsonNode = objectMapper.readTree(json);

    // 将 JsonNode 转换为 Object
    Person person = objectMapper.treeToValue(jsonNode, Person.class);
    System.out.println(person);

    // 将 Object 转换为 JsonNode
    JsonNode personJsonNode = objectMapper.valueToTree(person);

    // 得到节点值
    JsonNode firstName = personJsonNode.get("firstName");
    System.out.println("firstName = " + firstName.asText());
}

执行结果

Person(firstName=Chen, lastName=Ray, address=null, phoneNumberList=null)
firstName = Chen

Jackson JsonParser 和 JsonGenerator

除了 ObjectMapper 和 JsonNode 外,Jackson 还提供了更底层 JsonParser 和 JsonGenerator 来读写 JSON。

/**
 * JsonGenerator
 */
@Test
void jsonGenerator() throws IOException {
    // 实例化 JsonFactory 和 JsonParser 对象
    JsonFactory factory = new JsonFactory();
    JsonGenerator generator = factory.createGenerator(new File("D:\people.json"), JsonEncoding.UTF8);

    // 生成 json
    generator.writeStartObject();
    generator.writeStringField("firstName", "Chen");
    generator.writeStringField("lastName", "Ray");
    generator.writeNumberField("age", 18);
    generator.writeEndObject();
    generator.close();
}

/**
 * JsonParser
 */
@Test
void jsonParser() throws IOException {
    // json 消息
    String json = "{"firstName":"Chen","lastName":"Ray","age":18}";

    // 实例化 JsonFactory 和 JsonParser 对象
    JsonFactory factory = new JsonFactory();
    JsonParser parser = factory.createParser(json);

    // 解析 json
    while (!parser.isClosed()) {
        JsonToken jsonToken = parser.nextToken();

        if (JsonToken.FIELD_NAME.equals(jsonToken)) {
            String fieldName = parser.getCurrentName();
            jsonToken = parser.nextToken();
            System.out.println("fieldName = " + fieldName);
            System.out.println("jsonToken = " + jsonToken);
            System.out.println("parser.getValueAsString = " + parser.getValueAsString());
            System.out.println("------------------------------------");
        }
    }
}

执行结果 jsonGenerator() 方法在 D 盘生成 people.json 文件。

jsonParser() 方法结果

fieldName = firstName
jsonToken = VALUE_STRING
parser.getValueAsString = Chen
------------------------------------
fieldName = lastName
jsonToken = VALUE_STRING
parser.getValueAsString = Ray
------------------------------------
fieldName = age
jsonToken = VALUE_NUMBER_INT
parser.getValueAsString = 18
------------------------------------

处理 XML 数据

引入 JAR 包

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.10.1</version>
</dependency>

它依赖于下列一系列的包

jackson-annotations-xxx.jar
jackson-core-xxx.jar
jackson-databind-xxx.jar
 
jackson-module-jaxb-annotations-xxx.jar
 
stax2-api-xxx.jar
woodstox-core-xxx.jar

准备实体类

@Data
public class GradeDomain {

    @JacksonXmlProperty(localName = "gradeId", isAttribute = true)
    private int gradeId;

    @JacksonXmlText
    private String gradeName;
}
  • @JacksonXmlProperty注解通常可以不需要,若不用,生成xml标签名称就是实体类属性名称。但是如果你与XML节点上的不一致,那么必须加这个注解,并且注解的localName填上你想要的节点名字。最重要的是!实体类原来的属性必须首字母小写!否则会被识别成两个不同的属性。注解的isAttribute,确认是否为节点的属性,如上面“gradeId”。
  • @JacksonXmlText,用实体类属性上,说明该属性是否为简单内容,如果是,那么生成xml时,不会生成对应标签名称
@Data
public class ScoreDomain {

    @JacksonXmlProperty(localName = "scoreName")
    @JacksonXmlCData
    private String name;

    @JacksonXmlProperty(localName = "scoreNumber")
    private int score;
}
@Data
@JacksonXmlRootElement(localName = "student")
public class StudentDomain {

    @JsonIgnore
    private String studentName;

    @JacksonXmlProperty(localName = "age")
    @JacksonXmlCData
    private int age;

    @JacksonXmlProperty(localName = "grade")
    private GradeDomain grade;

    @JacksonXmlElementWrapper(localName = "scoreList")
    @JacksonXmlProperty(localName = "score")
    private List<ScoreDomain> scores;
}
  • @JacksonXmlRootElement用于类名上,表示xml最外层的根节点。注解中有localName属性,该属性如果不设置,那么生成的XML最外面就是Clazz.
  • @JacksonXmlElementWrapper一般用于list,list外层的标签。若不用的话,useWrapping = false
  • @JsonIgnore,忽略该实体类的属性,该注解是用于实体类转json的,但用于转xml一样有效,具体原因个人推测是XmlMapper是ObjectMapper的子类。

测试示例

/**
 * XmlMapper 示例
 */
@Test
void xmlMapper() throws IOException {
    // 实例化 XmlMapper 对象(ObjectMapper 子类)
    ObjectMapper xmlMapper = new XmlMapper();
    // XmlMapper 配置
    // 反序列化时,若实体类没有对应的属性,是否抛出JsonMappingException异常,false忽略掉
    xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    // 序列化是否绕根元素,true,则以类名为根元素
    xmlMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
    // 忽略空属性
    xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    // XML标签名:使用骆驼命名的属性名
    xmlMapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
    // 设置转换模式
    xmlMapper.enable(MapperFeature.USE_STD_BEAN_NAMING);

    // object 转 xml
    String xml = xmlMapper.writeValueAsString(newStudentDomain());
    xmlMapper.writeValue(new File("D:\student.xml"), newStudentDomain());
    System.out.println(xml);

    // xml 转 object
    StudentDomain studentDomain = xmlMapper.readValue(xml, StudentDomain.class);
    System.out.println(studentDomain);
}

private static StudentDomain newStudentDomain() {
    GradeDomain gradeDomain = new GradeDomain();
    gradeDomain.setGradeId(1);
    gradeDomain.setGradeName("高三");

    ScoreDomain score1 = new ScoreDomain();
    score1.setName("语文");
    score1.setScore(90);

    ScoreDomain score2 = new ScoreDomain();
    score2.setName("数学");
    score2.setScore(80);

    ScoreDomain score3 = new ScoreDomain();
    score3.setName("英语");
    score3.setScore(60);

    List<ScoreDomain> scoreDomains = Arrays.asList(score1, score2, score3);

    StudentDomain studentDomain = new StudentDomain();
    studentDomain.setStudentName("张三");
    studentDomain.setAge(18);
    studentDomain.setGrade(gradeDomain);
    studentDomain.setScores(scoreDomains);

    return studentDomain;
}

执行结果

// object 转 xml
<student><age>18</age><grade gradeId="1">高三</grade><scoreList><score><scoreName><![CDATA[语文]]></scoreName><scoreNumber>90</scoreNumber></score><score><scoreName><![CDATA[数学]]></scoreName><scoreNumber>80</scoreNumber></score><score><scoreName><![CDATA[英语]]></scoreName><scoreNumber>60</scoreNumber></score></scoreList></student>

// xml 转 object
StudentDomain(studentName=null, age=18, grade=GradeDomain(gradeId=1, gradeName=高三), scores=[ScoreDomain(name=语文, score=90), ScoreDomain(name=数学, score=80), ScoreDomain(name=英语, score=60)])

Jackson 项目实战 - JSON

记录一次项目中使用到 Jackson 操作的过程。

注意:Jackson 使用到的包是 org.codehaus.jackson,并非 xml 包

接口数据

这是从接口中获取的数据,其中很多属性是不需要的

{
    "data": {
        "pageNo": 1,
        "pageSize": 2,
        "count": 29,
        "list": [
            {
                "id": "f438f2d844e1486bba986d880ebec220",
                "remarks": null,
                "createDate": "2018-12-08 18:55:31",
                "updateDate": "2020-01-20 10:23:36",
                "delFlag": "0",
                "name": "广东xxx",
                "linkMan": "闫xx",
                "telphone": "188xxxxxxxx",
                "brand": null,
                "modelnum": null,
                "attrBak1": "1",
                "attrBak2": null,
                "attrBak3": null,
                "address": "佛山市顺德区xxx",
                "emailbox": "xx@qq.com",
                "orgunitid": null,
                "businessLicense": "xxx326U",
                "unifiedid": "xxx326U",
                "area": null,
                "user": null,
                "contacts": null,
                "tel": null,
                "companyCode": null,
                "ztAuditrecordList": [],
                "certificateList": null,
                "againEdit": null,
                "oldName": null,
                "procInsId": null
            },
            {
                "id": "92a2e5f40f7f49f1b45f80aa94d9a7f5",
                "remarks": null,
                "createDate": "2019-03-15 13:22:52",
                "updateDate": "2020-01-17 11:33:58",
                "delFlag": "0",
                "name": "湖南cc公司",
                "linkMan": "吴c",
                "telphone": "186cccccccc",
                "brand": null,
                "modelnum": null,
                "attrBak1": "1",
                "attrBak2": null,
                "attrBak3": null,
                "address": "湖南省长沙市ccc",
                "emailbox": "cc@ccc.com",
                "orgunitid": null,
                "businessLicense": "ccc690",
                "unifiedid": null,
                "area": null,
                "user": null,
                "contacts": null,
                "tel": null,
                "companyCode": null,
                "ztAuditrecordList": [],
                "certificateList": null,
                "againEdit": null,
                "oldName": null,
                "procInsId": null
            }
        ],
        "firstResult": 0,
        "maxResults": 2,
        "html": ""
    },
    "success": true,
    "errorCode": -1,
    "msg": "success"
}

工具类

HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和协议。 Android已成功集成了HttpClient,这意味这开发人员可以直接在Android应用中使用HtppClient来提交请求、接收响应

这里使用到 HttpClient 获取数据

/**
 * 描述:HttpClient
 *
 * @author Ray
 * @create 2019/11/14
 * @since 1.0.0
 */
public class HttpClientUtils {

    // POST 请求
    public static String HttpPostWithJson(String url, String json) {
        String returnValue = "这是默认返回值,接口调用失败";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        try{
            //第一步:创建HttpClient对象
            httpClient = HttpClients.createDefault();

            //第二步:创建httpPost对象
            HttpPost httpPost = new HttpPost(url);


            //第三步:给httpPost设置JSON格式的参数
            StringEntity requestEntity = new StringEntity(json,"utf-8");
            requestEntity.setContentEncoding("UTF-8");
            httpPost.setHeader("Content-type", "application/json");
            httpPost.setEntity(requestEntity);

            //第四步:发送HttpPost请求,获取返回值
            returnValue = httpClient.execute(httpPost,responseHandler); //调接口获取返回值时,必须用此方法

        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //第五步:处理返回值
        return returnValue;
    }

}

实体类

由于我只要其中的一部分的值,所以在类的上加上注解:@JsonIgnoreProperties(ignoreUnknown = true) 表示遇到没有的内容直接跳过就好。如果没有的话就会报错。

/**
 * 渣土_终端供应商Entity
 * @author Ray
 * @version 2020-02-24
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ZtTerminalProducer extends DataEntity<ZtTerminalProducer> {
	
	private static final long serialVersionUID = 1L;
	private String name;		// 供应商名
	private String linkMan;		// 联系人
	private String telphone;		// 电话
	private String address;		// 地址
	private String emailbox;		// 邮箱
	private String unifiedid;		// unifiedid
	private String businessLicense;		// business_license
	
	public ZtTerminalProducer() {
		super();
	}

	public ZtTerminalProducer(String id){
		super(id);
	}

	@ExcelField(title="供应商名", align=2, sort=1)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	@ExcelField(title="联系人", align=2, sort=2)
	public String getLinkMan() {
		return linkMan;
	}

	public void setLinkMan(String linkMan) {
		this.linkMan = linkMan;
	}
	
	@ExcelField(title="电话", align=2, sort=3)
	public String getTelphone() {
		return telphone;
	}

	public void setTelphone(String telphone) {
		this.telphone = telphone;
	}
	
	@ExcelField(title="地址", align=2, sort=4)
	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}
	
	@ExcelField(title="邮箱", align=2, sort=5)
	public String getEmailbox() {
		return emailbox;
	}

	public void setEmailbox(String emailbox) {
		this.emailbox = emailbox;
	}
	
	@ExcelField(title="unifiedid", align=2, sort=6)
	public String getUnifiedid() {
		return unifiedid;
	}

	public void setUnifiedid(String unifiedid) {
		this.unifiedid = unifiedid;
	}
	
	@ExcelField(title="business_license", align=2, sort=7)
	public String getBusinessLicense() {
		return businessLicense;
	}

	public void setBusinessLicense(String businessLicense) {
		this.businessLicense = businessLicense;
	}
	
}

如果项目中的 createDateupdateDate 是自动填充的,可以加上注解 @org.codehaus.jackson.annotate.JsonIgnore

protected 可以被子类,同package下类使用,不对外公开的访问修饰符。

protected String remarks;	// 备注
protected User createBy;	// 创建者
@org.codehaus.jackson.annotate.JsonIgnore
protected Date createDate;	// 创建日期
protected User updateBy;	// 更新者
@org.codehaus.jackson.annotate.JsonIgnore
protected Date updateDate;	// 更新日期
protected String delFlag; 	// 删除标记(0:正常;1:删除;2:审核)

Jackson 操作

/**
 * 获取终端运营商列表
 */
public void pullTerminalProducer() throws IOException {

    // 计时器
    TimeInterval timer = DateUtil.timer();

    // 1. 实例化 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();

    // 2. 获取数据
    String terminalProducerJson = HttpClientUtils.HttpPostWithJson(TERMINAL_PRODUCER_URL + pullLoginVerification() + "?pageSize=-1", "");

    // 3. 读取 json 的数据转换为节点对象
    JsonNode jsonNode = objectMapper.readTree(terminalProducerJson);

    // 4. 从节点读取数组中的指定内容
    JsonNode value = jsonNode.findValue("list");

    // 5. 将 json 的数组转化为对象数组
    List<ZtTerminalProducer> terminalProducerList = objectMapper.readValue(value.toString(), new TypeReference<List<ZtTerminalProducer>>() {});

    for (ZtTerminalProducer t :
            terminalProducerList) {
        ztTerminalProducerService.save(t);
    }

    // 花费时间
    logger.debug("pullTerminalProducer() 花费时间: [{}]", formattime(timer.interval()));
}

这样就可以直接读取 list 中的内容,同时对于 list 中不存在的属性或标记注解的属性,也可以忽略。

Jackson 项目实战 - XML

记录一次项目中使用到 Jackson 对 XML 的操作的过程。

注意:Jackson 使用到的包是 com.fasterxml.jackson,并非原来解析 json 包

<!-- 解析 xml -->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>${jackson.version}</version>
</dependency>

分析接口数据

这是通过接口获得的部分数据

<?xml version="1.0" encoding="UTF-8" ?>
<response>
    <head>
        <code>200</code>
        <message></message>
    </head>
    <body>
        <TotalPageCount>1</TotalPageCount>
        <TotalRecordCount>899</TotalRecordCount>
        <GeoManages>
            <GeoManage>
                <GID>A1</GID>
                <GeoName>A2</GeoName>
                <GeoFullName>A3</GeoFullName>
                <GeoType>A4</GeoType>
                <GeoCode>A5</GeoCode>
                <GeoDist>A6</GeoDist>
                <GeoJSON>A7</GeoJSON>
                <RefreshDate>A8</RefreshDate>
                <RefreshClerk>A9</RefreshClerk>
                <BusinessID>A10</BusinessID>
            </GeoManage>
            <GeoManage>
                <GID>B1</GID>
                <GeoName>B2</GeoName>
                <GeoFullName>B3</GeoFullName>
                <GeoType>B4</GeoType>
                <GeoCode>B5</GeoCode>
                <GeoDist>B6</GeoDist>
                <GeoJSON>B7</GeoJSON>
                <RefreshDate>B8</RefreshDate>
                <RefreshClerk>B9</RefreshClerk>
                <BusinessID>B10</BusinessID>
            </GeoManage>
        </GeoManages>
    </body>
</response>

这部分的数据,我们真正需要的是 <GeoManage> 里面的内容,所以其他都是多余的。在 JSON 中我们可以使用 JsonNode 读取 JSON 的数据转换为节点对象,但是 XML 中读取失败,暂时还没找到原因。

所以我的思路是,将 XML 文件多余的部分删除,留下有用的,删除后的结构如下:

        <GeoManages>
            <GeoManage>
                <GID>A1</GID>
                <GeoName>A2</GeoName>
                <GeoFullName>A3</GeoFullName>
                <GeoType>A4</GeoType>
                <GeoCode>A5</GeoCode>
                <GeoDist>A6</GeoDist>
                <GeoJSON>A7</GeoJSON>
                <RefreshDate>A8</RefreshDate>
                <RefreshClerk>A9</RefreshClerk>
                <BusinessID>A10</BusinessID>
            </GeoManage>
            <GeoManage>
                <GID>B1</GID>
                <GeoName>B2</GeoName>
                <GeoFullName>B3</GeoFullName>
                <GeoType>B4</GeoType>
                <GeoCode>B5</GeoCode>
                <GeoDist>B6</GeoDist>
                <GeoJSON>B7</GeoJSON>
                <RefreshDate>B8</RefreshDate>
                <RefreshClerk>B9</RefreshClerk>
                <BusinessID>B10</BusinessID>
            </GeoManage>
        </GeoManages>
    </body>
</response>

多余的 </body></response> 没有关系,XML 元素是可扩展,以携带更多的信息(其实是数据量太大,处理异常了)。。

实例中的根元素是 <GeoManages>。文档中的所有 <GeoManage> 元素都被包含在 <GeoManages> 中。 <GeoManage> 元素有 10 个子元素:<GID><GeoName><GeoFullName><GeoType> 等。

注意:如果根元素换成 <GeoManage>,获取到的数据会丢失一部分。测试 899 条记录只获得 10 条记录。

工具类

/**
 * 描述:httpclient工具类
 *
 * @author Ray
 * @create 2020/2/25
 * @since 1.0.0
 */
@Slf4j
public class HttpRequestUtil {

    /**
     * 发送get请求
     * @param url
     * @param decodeCharset
     * @return
     */
    public static String sendGetRequest(String url, String decodeCharset) {
        HttpClient httpclient = new DefaultHttpClient();
        String responseContent = null;
        HttpGet httpGet = new HttpGet(url);
        HttpEntity entity = null;
        try {
            HttpResponse response = httpclient.execute(httpGet);
            System.out.println(response);
            entity = response.getEntity();
            if (null != entity) {
                responseContent = EntityUtils.toString(entity, decodeCharset == null ? "UTF-8" : decodeCharset);
            }
        } catch (Exception e) {
            log.error("该异常通常是网络原因引起的,如HTTP服务器未启动等,堆栈信息如下", e);
        } finally {
            try {
                EntityUtils.consume(entity);
                httpclient.getConnectionManager().shutdown();
            } catch (Exception ex) {
                log.error("net io excepiton", ex);
            }
        }
        return responseContent;
    }

    /**
     * post 请求
     * @param reqURL
     * @param  data  可以为param="key1=value1&key2=value2"的一串字符串,或者是jsonObject
     * @return
     */
    public static String sendHttpPostRequest(String reqURL, String data) {
        HttpClient httpclient = new DefaultHttpClient();
        String respStr = "";
        try {
            HttpPost httppost = new HttpPost(reqURL);
            StringEntity strEntity = new StringEntity(data, "UTF-8");
            strEntity.setContentType("application/x-www-form-urlencoded");
            httppost.setEntity(strEntity);
            log.info(EntityUtils.toString(strEntity));
            log.info("executing request " + httppost.getRequestLine());

            HttpResponse response = httpclient.execute(httppost);
            HttpEntity resEntity = response.getEntity();

            if (resEntity != null) {
                log.info("返回数据长度: " + resEntity.getContentLength());
                respStr = EntityUtils.toString(resEntity);
                log.info("respStr " + respStr);
            }

        } catch (ClientProtocolException e) {
            log.error("sendHttpPostRequest : " ,e);
        } catch (IOException e) {
            log.error("sendHttpPostRequest : " ,e);
        } finally {
            httpclient.getConnectionManager().shutdown();
        }
        return respStr;
    }

    /**
     * 发送post请求
     * @param url
     * @param params
     * @return
     * @throws Exception
     */
    public static String sendHttpPostRequest(String url, Map<String, String> params) {
        String respStr = "";
        HttpClient httpclient = new DefaultHttpClient();
        httpclient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);
        httpclient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
        log.info("url: " + url);
        log.info("params: " + params);
        try {
            HttpPost post = new HttpPost(url);
            List<BasicNameValuePair> postData = new ArrayList<BasicNameValuePair>();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                postData.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(postData, "UTF-8");
            post.setEntity(entity);
            HttpResponse response = httpclient.execute(post);
            HttpEntity resEntity = response.getEntity();
            if (resEntity != null) {
                log.info("返回数据长度: " + resEntity.getContentLength());
                respStr = EntityUtils.toString(resEntity);
                log.info("respStr " + respStr);
            }
        } catch (ClientProtocolException e) {
            log.error("sendHttpPostRequest : " +e);
        } catch (IOException e) {
            log.error("sendHttpPostRequest : " +e);
        } finally {
            httpclient.getConnectionManager().shutdown();
        }
        return respStr;
    }

    /**
     * 测试
     * @param args
     */
    public static void main(String[] args) {
        String params = "bankcard=6217856101018144878&key=316fcfd892e7e4d24ded8699f1f7d957";
        String resultStr = HttpRequestUtil.sendHttpPostRequest("http://apis.juhe.cn/bankcardcore/query", params);
        System.out.println(resultStr);
        ObjectMapper mapper = new ObjectMapper();
        try {
            Map map = mapper.readValue(resultStr, Map.class);
            Set<Map.Entry> set = map.entrySet();
            for (Map.Entry entry : set) {
                System.out.println(entry.getKey() + "==" + entry.getValue());
            }
        }catch(IOException ioe){
            ioe.printStackTrace();
        }
    }

}

准备实体类

/**
 * 获取已绘制区域列表信息Entity
 * @author Ray
 * @version 2019-11-15
 */
@JacksonXmlRootElement(localName = "GeoManage")
@JsonIgnoreProperties(ignoreUnknown = true)
public class ZtGeoManage extends DataEntity<ZtGeoManage> {
	
	private static final long serialVersionUID = 1L;
	@JacksonXmlProperty(localName = "GID")
	private String geoId;		// 区域ID
	@JacksonXmlProperty(localName = "GeoName")
	private String geoName;		// 区域简称
	@JacksonXmlProperty(localName = "GeoFullName")
	private String geoFullName;		// 区域全称
	@JacksonXmlProperty(localName = "GeoType")
	private String geoType;		// 区域类型
	@JacksonXmlProperty(localName = "GeoCode")
	private String geoCode;		// 区域编号
	@JacksonXmlProperty(localName = "GeoDist")
	private String geoDist;		// 区域区县
	@JacksonXmlProperty(localName = "GeoJSON")
	private String geoJson;		// 区域地理信息
	//@JacksonXmlProperty(localName = "RefreshDate") // 这个Date类型需要转格式,先忽略
	@JsonIgnore
	private Date refreshDate;		// 绘制地理信息时间
	@JacksonXmlProperty(localName = "RefreshClerk")
	private String refreshClerk;		// 绘制地理信息人
	@JacksonXmlProperty(localName = "BusinessID")
	private String businessId;		// 区域业务ID
	@JsonIgnore
	private Date synchroDate;		// 同步时间(区分同一批记录)
	
	public ZtGeoManage() {
		super();
	}

	public ZtGeoManage(String id){
		super(id);
	}

	@ExcelField(title="区域ID", align=2, sort=1)
	public String getGeoId() {
		return geoId;
	}

	public void setGeoId(String geoId) {
		this.geoId = geoId;
	}
	
	@ExcelField(title="区域简称", align=2, sort=2)
	public String getGeoName() {
		return geoName;
	}

	public void setGeoName(String geoName) {
		this.geoName = geoName;
	}
	
	@ExcelField(title="区域全称", align=2, sort=3)
	public String getGeoFullName() {
		return geoFullName;
	}

	public void setGeoFullName(String geoFullName) {
		this.geoFullName = geoFullName;
	}
	
	@ExcelField(title="区域类型", align=2, sort=4)
	public String getGeoType() {
		return geoType;
	}

	public void setGeoType(String geoType) {
		this.geoType = geoType;
	}
	
	@ExcelField(title="区域编号", align=2, sort=5)
	public String getGeoCode() {
		return geoCode;
	}

	public void setGeoCode(String geoCode) {
		this.geoCode = geoCode;
	}
	
	@ExcelField(title="区域区县", align=2, sort=6)
	public String getGeoDist() {
		return geoDist;
	}

	public void setGeoDist(String geoDist) {
		this.geoDist = geoDist;
	}
	
	@ExcelField(title="区域地理信息", align=2, sort=7)
	public String getGeoJson() {
		return geoJson;
	}

	public void setGeoJson(String geoJson) {
		this.geoJson = geoJson;
	}
	
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	@ExcelField(title="绘制地理信息时间", align=2, sort=8)
	public Date getRefreshDate() {
		return refreshDate;
	}

	public void setRefreshDate(Date refreshDate) {
		this.refreshDate = refreshDate;
	}
	
	@ExcelField(title="绘制地理信息人", align=2, sort=9)
	public String getRefreshClerk() {
		return refreshClerk;
	}

	public void setRefreshClerk(String refreshClerk) {
		this.refreshClerk = refreshClerk;
	}
	
	@ExcelField(title="区域业务ID", align=2, sort=10)
	public String getBusinessId() {
		return businessId;
	}

	public void setBusinessId(String businessId) {
		this.businessId = businessId;
	}
	
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	@NotNull(message="同步时间(区分同一批记录)不能为空")
	@ExcelField(title="同步时间(区分同一批记录)", align=2, sort=11)
	public Date getSynchroDate() {
		return synchroDate;
	}

	public void setSynchroDate(Date synchroDate) {
		this.synchroDate = synchroDate;
	}
	
}

说明:

@JacksonXmlRootElement:指定生成xml根标签的名字;

@JacksonXmlElementWrapper:可用于指定List等集合类,外围标签名;

@JacksonXmlProperty:指定包装标签名,或者指定标签内部属性名;

测试示例

/**
 * 获取已绘制的区域列表信息 - 新
 * 2020-2-25 21:25:42
 */
public void pullGeoManageNew() throws IOException {

    // 传递参数
    Map<String, String> params = new HashMap(2);
    params.put("PageSize", "0");

    // 经测试,直接传String,会参数错误,要注意接口是使用url发送还是json发送
    //String params = "PageSize=0";

    String geoManageJson = HttpRequestUtil.sendHttpPostRequest(PULL_GEO_MANAGE_URL, objectMapper.writeValueAsString(params));

    // 如果有 html 特殊符号(只列出部分),需转换,不然无法解析,没有需要的可以注释
    geoManageJson = geoManageJson.replaceAll("&ldquo;", "“");
    geoManageJson = geoManageJson.replaceAll("&rdquo;", "”");
    geoManageJson = geoManageJson.replaceAll("&mdash;", "——");
    geoManageJson = geoManageJson.replaceAll("&deg;", "°");
    geoManageJson = geoManageJson.replaceAll("&euro;", "");
    geoManageJson = geoManageJson.replaceAll("&middot;", "·");

    // 截取?之后字符串
    String strSub = geoManageJson.substring(0, geoManageJson.indexOf("<GeoManage>"));
    geoManageJson = geoManageJson.substring(strSub.length());

    // 实例化 XmlMapper 对象(ObjectMapper 子类)
    com.fasterxml.jackson.databind.ObjectMapper xmlMapper = new XmlMapper();

    /** 下面的配置没经过测试 */
    //// 反序列化时,若实体类没有对应的属性,是否抛出JsonMappingException异常,false忽略掉
    //xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    //// 序列化是否绕根元素,true,则以类名为根元素
    //xmlMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
    //// 设置让jackson支持转义
    //xmlMapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
    //// 允许出现特殊字符和转义符
    //xmlMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true) ;
    //// 允许出现单引号
    //xmlMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) ;
    //// 忽略空属性
    //xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    //// XML标签名:使用骆驼命名的属性名
    //xmlMapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
    //// 设置转换模式
    //xmlMapper.enable(MapperFeature.USE_STD_BEAN_NAMING);

    // XML -> List (可用)
    List<ZtGeoManage> ztGeoManageList = xmlMapper.readValue(geoManageJson, new com.fasterxml.jackson.core.type.TypeReference<List<ZtGeoManage>>() {});
    System.out.println("ztGeoManageList = " + ztGeoManageList);

    // XML -> Array (可用)
    ZtGeoManage[] arrays = xmlMapper.readValue(geoManageJson, ZtGeoManage[].class);
    System.out.println("arrays = " + arrays);

}

执行结果

2020-02-26 08:23:56,055 INFO  [com.jeeplus.common.utils.HttpRequestUtil] - {"PageSize":"0"}
2020-02-26 08:23:56,067 INFO  [com.jeeplus.common.utils.HttpRequestUtil] - executing request POST http://120.78.xx.158:80xx/ups/ztpls/bl/PullGeoManage?authtoken=0024441E-9B7E-4EB7-xxxx-D222A48C795F HTTP/1.1
2020-02-26 08:23:58,717 INFO  [com.jeeplus.common.utils.HttpRequestUtil] - 返回数据长度: 718497
ztGeoManageList = [com.jeeplus.modules.ztfx.entity.ZtGeoManage@3f3c8b60[geoId=A1,geoName=A2,geoFullName=A3,geoType=A4,geoCode=A5,geoDist=A6,geoJson=A7,refreshDate=A8,refreshClerk=A9,businessId=A10,synchroDate=<null>,remarks=<null>,createBy=<null>,createDate=<null>,updateBy=<null>,updateDate=<null>,delFlag=0,id=<null>,currentUser=<null>,page=<null>,sqlMap=<null>,isNewRecord=false], com.jeeplus.modules.ztfx.entity.ZtGeoManage@3f357c9d[geoId=B1,geoName=B2,geoFullName=B3,geoType=B4,geoCode=B5,geoDist=B6,geoJson=B7,refreshDate=B8,refreshClerk=B9,businessId=B10,synchroDate=<null>,remarks=<null>,createBy=<null>,createDate=<null>,updateBy=<null>,updateDate=<null>,delFlag=0,id=<null>,currentUser=<null>,page=<null>,sqlMap=<null>,isNewRecord=false]]