详解Struts2中json 相互引用死循环解决办法

下面是详解Struts2中json 相互引用死循环解决办法的完整攻略。

简介

在 Struts2 中,使用 JSON 返回数据时,如果包含相互引用的对象,就会出现死循环的情况。这是因为在序列化时,对象互相引用,导致 Gson 序列化器无法判断对象的终止条件从而产生栈溢出。解决这个问题的方法是给予 JSON 序列化器一些帮助,在序列化时忽略相互引用的部分。

解决方案

1. 使用 Jackson 序列化器

Jackson 是一个高性能的 JSON 序列化库,它支持完全的 JSON 规范并且可以与大量的 Java 框架集成。在 Struts2 的配置文件中,将默认的 JSON 序列化器 Gson 改为 Jackson 即可解决相互引用的问题。

<constant name="struts.mapper.class" value="org.apache.struts2.dispatcher.mapper.JacksonJaxbJsonMapper"/>

2. 添加 Jackson 库对相互引用的支持

在上一步中,将默认的 JSON 序列化器改为了 Jackson,但是默认情况下 Jackson 也无法处理相互引用的问题。需要添加 Jackson 库对相互引用的支持。

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class User {

    private Long id;
    private String name;
    private Group group;

    // getters and setters
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class Group {

    private Long id;
    private String name;
    private List<User> users;

    // getters and setters
}

在类上添加 @JsonIdentityInfo 注解,其中 generator 属性指定了序列化时采用的引用生成器,property 属性指定了生成的引用标识属性。这样,在序列化时,只要检测到两个对象 id 相同,就会进行引用替换,从而避免了相互引用的死循环问题。

示例

例1:Gson 序列化器

public class User {

    private Long id;
    private String name;
    private Group group;

    // getters and setters
}

public class Group {

    private Long id;
    private String name;
    private List<User> users;

    // getters and setters
}

public class JsonAction extends ActionSupport {

    private List<Group> groups;

    // getters and setters

    public String execute() throws Exception {
        groups = new ArrayList<Group>();

        Group group1 = new Group();
        group1.setId(1L);
        group1.setName("Group 1");

        Group group2 = new Group();
        group2.setId(2L);
        group2.setName("Group 2");

        groups.add(group1);
        groups.add(group2);

        User user1 = new User();
        user1.setId(1L);
        user1.setName("User 1");
        user1.setGroup(group1);

        User user2 = new User();
        user2.setId(2L);
        user2.setName("User 2");
        user2.setGroup(group2);

        User user3 = new User();
        user3.setId(3L);
        user3.setName("User 3");
        user3.setGroup(group1);

        group1.setUsers(Arrays.asList(user1, user3));
        group2.setUsers(Arrays.asList(user2));

        return SUCCESS;
    }

    public String toJson() {
        HttpServletResponse response = ServletActionContext.getResponse();
        response.setContentType("application/json;charset=utf-8");

        Gson gson = new Gson();
        String json = gson.toJson(groups);

        try {
            response.getWriter().write(json);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}

上述示例中使用了 Gson 序列化器,在执行 toJson 方法时返回字串完全错误。出现死循环的原因是 Group 中包含了多个 User 对象,User 中包含了一个 Group,导致序列化时出现了循环引用。

例2:Jackson 序列化器

<constant name="struts.mapper.class" value="org.apache.struts2.dispatcher.mapper.JacksonJaxbJsonMapper"/>
public class User {

    private Long id;
    private String name;

    @JsonIgnoreProperties("users")
    private Group group;

    // getters and setters
}

public class Group {

    private Long id;
    private String name;

    @JsonManagedReference
    private List<User> users;

    // getters and setters
}

public class JsonAction extends ActionSupport {

    private List<Group> groups;

    // getters and setters

    public String execute() throws Exception {
        groups = new ArrayList<Group>();

        Group group1 = new Group();
        group1.setId(1L);
        group1.setName("Group 1");

        Group group2 = new Group();
        group2.setId(2L);
        group2.setName("Group 2");

        groups.add(group1);
        groups.add(group2);

        User user1 = new User();
        user1.setId(1L);
        user1.setName("User 1");
        user1.setGroup(group1);

        User user2 = new User();
        user2.setId(2L);
        user2.setName("User 2");
        user2.setGroup(group2);

        User user3 = new User();
        user3.setId(3L);
        user3.setName("User 3");
        user3.setGroup(group1);

        group1.setUsers(Arrays.asList(user1, user3));
        group2.setUsers(Arrays.asList(user2));

        return SUCCESS;
    }

    public String toJson() {
        HttpServletResponse response = ServletActionContext.getResponse();
        response.setContentType("application/json;charset=utf-8");

        ObjectMapper mapper = new ObjectMapper();
        String json = null;

        try {
            json = mapper.writeValueAsString(groups);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            response.getWriter().write(json);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}

上述示例中使用了 Jackson 序列化器,在执行 toJson 方法时正确地序列化了数据,避免了死循环的问题。在 User 类中,使用了 @JsonIgnoreProperties 注解,排除了序列化 users 属性,从而避免了嵌套序列化的问题。在 Group 类中,使用了 @JsonManagedReference 注解,指定了 users 属性为父对象,在反序列化时会自动添加到父对象中。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Struts2中json 相互引用死循环解决办法 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • 趣谈Unicode、Ascii、utf-8、GB2312、GBK等编码知识

    趣谈Unicode、ASCII、UTF-8、GB2312、GBK等编码知识 什么是编码? 计算机是一台二进制数处理机器,它无法直接处理人类可读的字符和文本。因此,需要通过一种规范来将字符和文本转化为计算机可识别的二进制数,这个规范就叫做编码。 ASCII编码 ASCII编码,全称是American Standard Code for Information …

    Java 2023年5月20日
    00
  • Java多个版本切换的几种方法

    很高兴为你提供“Java多个版本切换的几种方法”的完整攻略。 前言 在我们进行Java开发的时候,经常需要在多个Java版本之间进行切换。为了实现这种切换,在这篇文章中,我们将介绍Java多个版本切换的几种方法。 方法一:使用Jenv Jenv是一个非常好的Java版本管理工具。它能够让你更方便地切换不同的Java版本。下面是使用Jenv进行Java版本切换…

    Java 2023年5月20日
    00
  • 使用Netty进行编解码的操作过程详解

    使用Netty进行编解码是网络编程中的一个重要的环节。下面我将详细讲解使用Netty进行编解码的操作过程,并且提供两个示例。 Netty编解码的操作过程 第一步:定义消息实体类(Message) 在进行Netty编解码的操作之前,我们需要定义一个消息实体类(Message),该实体类需要实现Serializable接口。代码示例如下: public clas…

    Java 2023年5月20日
    00
  • 浅谈servlet3异步原理与实践

    浅谈servlet3异步原理与实践 什么是Servlet3异步 Servlet3.0规范中增加了异步处理的功能,使Servlet容器的性能可以进一步提升。Servlet3.0之前,servlet都是由线程来处理的,每次请求都需要创建一个线程,处理完请求后才会销毁这个线程。如果请求量很大,反复创建销毁线程的过程会给服务器造成很大负担。 而异步Servlet能够…

    Java 2023年5月20日
    00
  • Java Map集合使用方法全面梳理

    Java Map集合使用方法全面梳理 什么是Java Map集合 在Java编程中,Map是一个非常有用的接口,它可以帮助我们管理具有键值对的数据集合。具体来说,一个Map对象是由一组键值对组成,其中每个键(key)都必须是唯一且不变的,而每个值(value)可以是任意对象(包括null)。Map中的键值对是没有固定的顺序的。 Java提供了多种不同的Map…

    Java 2023年6月15日
    00
  • Java8中 LocalDate和java.sql.Date的相互转换操作

    Java 8中提供了新的日期时间API,其中非常重要的一部分是LocalDate类。在某些情况下需要将LocalDate转换为java.sql.Date,或者将java.sql.Date转换为LocalDate。接下来,我们来详细讲解Java 8中LocalDate和java.sql.Date的相互转换操作。 1. 将LocalDate转换为java.sql…

    Java 2023年6月1日
    00
  • Java反射技术详解

    Java反射技术详解 什么是Java反射技术 Java反射技术是一种可以在运行时获取类的属性和方法的一种能力。通过Java反射技术,我们可以在运行时动态地创建对象、调用方法、获取属性以及修改属性。 如何使用Java反射技术 使用Java反射技术的第一步就是获取需要操作的类的Class对象。通过Class对象,我们可以获取这个类的所有属性和方法,并对它们进行操…

    Java 2023年5月26日
    00
  • Java中Timer的schedule()方法参数详解

    Java中的Timer类提供了schedule()方法,该方法可以在指定的延迟之后安排指定的任务执行。schedule()方法有多种参数组合,下面来详细讲解它的参数及其含义。 一、语法 public void schedule(TimerTask task, long delay, long period) public void schedule(Time…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部