【问题标题】:How to implement API to return a nested JSON?如何实现 API 以返回嵌套的 JSON?
【发布时间】:2020-04-06 12:11:13
【问题描述】:

我对 API 完全陌生,最近使用 Spring Boot 开发了我的第一个 API。到目前为止,我已经实现了将 h2 内存数据库中的所有记录作为 Json 列表检索,但是因为我将尝试使用 React 获取它们并使用 D3 在树形图中显示它们,所以我还需要获取它们采用嵌套的 JSON 格式。

这是我目前的代码:

外星人.java

package com.example.alien.model;
import java.util.List;
import java.util.Optional;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import com.fasterxml.jackson.annotation.JsonIgnore;

@Entity
public class Alien {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String name;
    private String type;
    private String planet;
    @JsonIgnore
    @ManyToOne(targetEntity = Alien.class, fetch=FetchType.LAZY)
    @JoinColumn(name="parentId")
    private Optional<Alien> parent;
    @JsonIgnore
    @OneToMany(targetEntity = Alien.class, cascade=CascadeType.ALL, mappedBy="parent", fetch=FetchType.LAZY)
    private List<Alien> children;

    public List<Alien> getChildren() {
        return children;
    }
    public void setChildren(List<Alien> children) {
        this.children = children;
    }
    public Optional<Alien> getParent() {
        return parent;
    }
    public void  setParent(Optional<Alien> parent) {
        this.parent = parent;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getPlanet() {
        return planet;
    }
    public void setPlanet(String planet) {
        this.planet = planet;
    }

}

AlienDto.java

package com.example.aliendto;

import java.util.Optional;

import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import com.example.alien.model.Alien;
import com.fasterxml.jackson.annotation.JsonIgnore;

public class AlienDto {
    private String name;
    private String type;
    private String planet;
    private Integer parentId = 0;

    public String getName() {
        return name;
    }

    public String getType() {
        return type;
    }

    public String getPlanet() {
        return planet;
    }

    public Integer getParentId() {
        return parentId;
    }
}

AlienController.java

package com.example.alien.controller;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.alien.dao.AlienRepo;
import com.example.alien.model.Alien;
import com.example.aliendto.AlienDto;

@CrossOrigin(origins = "*", allowedHeaders = "*")
@RestController
public class AlienController 
{   
    @Autowired
    AlienRepo repo;

    @PutMapping("/alien/{id}")
    public Alien updateAlien(@RequestBody Alien alien) {
        repo.save(alien);
        return alien;
    }

    @DeleteMapping("/alien/{id}")
    public String deleteAlien(@PathVariable Integer id)
    {
        Alien a = repo.getOne(id);
        repo.delete(a);
        return "deleted";
    }

     @PostMapping("/alien")
     public Alien addAlien(@RequestBody AlienDto dto) {
         Alien alien = new Alien();
         alien.setName(dto.getName());
         alien.setType(dto.getType());
         alien.setPlanet(dto.getPlanet());

         if(dto.getParentId() == 0) {
             alien.setParent(null);
         }else {
             Optional<Alien> parent = this.repo.findById(dto.getParentId());
             alien.setParent(parent);
         }
            repo.save(alien);
            return alien;
        } 

    @GetMapping("/aliens")
    public List<Alien> getAliens() {
        return repo.findAll();
    }

    @RequestMapping("/alien/{id}")
    public Optional<Alien> oneAlien(@PathVariable("id") Integer id) {
        return repo.findById(id);
    }

    @GetMapping("/")
    public String home()
    {
        return "do_Ob";
    }
}

AlienRepo.java

package com.example.alien.dao;


import org.springframework.data.jpa.repository.JpaRepository;

import com.example.alien.model.Alien;

public interface AlienRepo extends JpaRepository<Alien, Integer>
{
}

现在我在 localhost:8080/aliens 上得到这样的结果

[{"id":1,"name":"Javier","type":"Alpha","planet":"Earth","children":"Laia"}, 
 {"id":2,"name":"Javier","type":"Alpha","planet":"Earth","children":"John"},
{"id":3,"name":"Laia","type":"Omega","planet":"Earth","children":""},
{"id":4,"name":"John","type":"Omega","planet":"Earth","children":""}]]

但我也想让他们像这样走另一条路:

[{"name":"Javier",
  "type":"Alpha",
  "planet":"Earth",
  "children":[{"name": "Laia", "type":"Omega",....},
              {"name": "John", "type":"Omega",....}]]

如果我可以使用 React 将 JSON 列表转换为嵌套的 Json 也可以。

谢谢

编辑:

https://i.imgur.com/zuc8D6I.png - 邮递员输出

【问题讨论】:

    标签: java reactjs spring-boot rest


    【解决方案1】:

    您需要进行一些更改以使其分层,请检查如下:

    1. 添加额外的列“父”以维护层次结构

      @JsonIgnore
      @ManyToOne(targetEntity = Alien .class, fetch = FetchType.LAZY)
      @JoinColumn(name = "parentId")
      private Alien parent;
      
    2. 正如 Gaurav 所说,请将private String children; 更改如下

          @JsonIgnore
          @OneToMany(targetEntity = Alien .class, cascade = CascadeType.ALL, mappedBy = 
          "parent", fetch = FetchType.LAZY)
          private List<Alien> children;
      
    3. 由于我们添加了一个额外的列来维护层次结构,您还需要进行更改 在你 POST 和 PUT 中,在创建资源时你需要传递 parent_id

      if(alien.getParentId() == 0) { alien.setParent(null); }else { Alien parent = this.repo.findById(alien.getParentId()); alien.setParent(parent); }

    Edit : DTO,通常我们用来映射请求数据。

    要使用上面的代码,您需要将 requestbody 映射到 dto 对象,该对象将具有名称、其他属性、parentId,然后从 dto 创建“Alien”对象。

    a -> 新的 Dto

    公共类 AlientDto {

    private String name;
    private String type;
    private String planet;
    private int parentId = 0;
    
    // Getter Setter
    

    }

    b-> 控制器更改

     @PostMapping("/alien")
         public Alien addAlien(@RequestBody AlienDto dto) {
             Alien alien = new Alien();
             alien.name(dto.getName());
             alien.type(dto.getType());
             alien.planet(dto.getPlanet());
    
             if(dto.getParentId() == 0) {
                 alien.setParent(null);
             }else {
                 Alien parent = this.repo.findById(dto.getParentId());
                 alien.setParent(parent);
             }
                repo.save(alien);
                return alien;
            } 
    

    更新上面的代码如下

      Alien parent = this.repo.findById(<pass parent id here>); // parent id is will be any alien id
                            alien.setParent(parent);
                        } 
    

    你不能同时拥有 parent 和 parent_id

    Parent : 外星实体仅是其他外星人的父级

    parentId : 是父级的唯一 id

    例如:

    在这里我们创建了层次结构 Alien1 没有任何父母 Alien1 是 Alien2 的父级 Alien2 是 Alien3 和 Alien4 的父级

    【讨论】:

    • 非常感谢!我想我快要得到它了。请问findById里面的dto是什么?而且,为什么有时你使用 parentId 而其他人只是 parent?我不明白 parentId 在这里的作用。再次感谢。
    • 您好,添加您的代码后出现以下错误。 "实体映射中的重复列:com.example.alien.model.Alien 列:parent_id(应使用 insert="false" update="false" 进行映射)"
    • 我通过添加 @Column(name="parentId", insertable=false, updatable=false) 解决了这个问题
    • 是的,对不起,我错过了添加。更简洁的方法是创建具有所有必需属性的 dto 对象,然后将 dto 对象映射到外来实体。使用这种方法,您不需要实体中的这个 parent_id 字段
    • 你能得到想要的结果吗?
    【解决方案2】:

    不要将孩子作为字符串,而是将它们作为对象列表。 替换这个:

    private String children;
    

    List<Alien> children;
    

    和添加方法来添加孩子

    void addChild(Alien child) {
        if(this.children == null) {
            this.children = new ArrayList<>();
        }
        this.children.add(child);
    }
    

    假设层次结构是

    一个

    |_B

    |_C

    然后创建:

    Alien c - new Alien();
    Alien b = new Alien();
    Alien a = new Alien();
    
    b.addChild(c);
    a.addChild(b);
    

    【讨论】:

    • 谢谢,但我不知道如何通过这样做得到我想要的。如果孩子有自己的孩子怎么办?如何建立祖父母与孙子之间的关系?
    • 同样的方法,你可以在创建它的对象的同时添加children给children,我推荐你阅读tree 数据结构,它是一个简单的对象树,你甚至可以在java中搜索create tree 它会帮助
    • 您传递的是什么父 ID?您还可以检查一下它正在打印什么查询。
    • 更新了答案,如果你想要parent id,你可以在同一个类中取一个字段。
    猜你喜欢
    • 1970-01-01
    • 2015-02-03
    • 2019-05-20
    • 2020-10-23
    • 2021-04-25
    • 1970-01-01
    • 2017-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多