如何实现返回嵌套的JSON的API?

huangapple go评论84阅读模式
英文:

How to implement API to return a nested JSON?

问题

I'm completely new to APIs and recently developed my first one using Spring Boot. So far, I've achieved to retrieve all records in a h2 in-memory database as a list of Jsons but because I'll try to fetch them with React and display them in a tree graph using D3, I also need to get them in a nested JSON format.

This is my code so far:

Alien.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>
{
}

Now I get the results like this on 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":""}]

But I ALSO would like to get them in another route like this:

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

It'd also be okay if I could transform the JSON list to nested Json using React.

Thanks

Edit:

Postman Output

英文:

I'm completely new to APIs and recently developed my first one using Spring Boot. So far, I've achieved to retrieve all records in a h2 in-memory database as a list of Jsons but because I'll try to fetch them with React and display them in a tree graph using D3, I also need to get them in a nested JSON format.

This is my code so far:

Alien.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=&quot;parentId&quot;)
private Optional&lt;Alien&gt; parent;
@JsonIgnore
@OneToMany(targetEntity = Alien.class, cascade=CascadeType.ALL, mappedBy=&quot;parent&quot;, fetch=FetchType.LAZY)
private List&lt;Alien&gt; children;
public List&lt;Alien&gt; getChildren() {
return children;
}
public void setChildren(List&lt;Alien&gt; children) {
this.children = children;
}
public Optional&lt;Alien&gt; getParent() {
return parent;
}
public void  setParent(Optional&lt;Alien&gt; 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 = &quot;*&quot;, allowedHeaders = &quot;*&quot;)
@RestController
public class AlienController 
{	
@Autowired
AlienRepo repo;
@PutMapping(&quot;/alien/{id}&quot;)
public Alien updateAlien(@RequestBody Alien alien) {
repo.save(alien);
return alien;
}
@DeleteMapping(&quot;/alien/{id}&quot;)
public String deleteAlien(@PathVariable Integer id)
{
Alien a = repo.getOne(id);
repo.delete(a);
return &quot;deleted&quot;;
}
@PostMapping(&quot;/alien&quot;)
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&lt;Alien&gt; parent = this.repo.findById(dto.getParentId());
alien.setParent(parent);
}
repo.save(alien);
return alien;
} 
@GetMapping(&quot;/aliens&quot;)
public List&lt;Alien&gt; getAliens() {
return repo.findAll();
}
@RequestMapping(&quot;/alien/{id}&quot;)
public Optional&lt;Alien&gt; oneAlien(@PathVariable(&quot;id&quot;) Integer id) {
return repo.findById(id);
}
@GetMapping(&quot;/&quot;)
public String home()
{
return &quot;do_Ob&quot;;
}
}

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&lt;Alien, Integer&gt;
{
}

Now I get the results like this on localhost:8080/aliens

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

But I ALSO would like to get them in another route like this:

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

It'd also be okay if I could transform the JSON list to nested Json using React.

Thanks

Edit:

https://i.stack.imgur.com/FZoTI.png - Postman Output

答案1

得分: 1

你需要进行一些更改以使其具有层次结构,请按照以下方式检查:

  1. 添加额外的列'parent'以维护层次结构

     @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); }

编辑:DTO,通常我们用于映射请求数据。

要使上述代码工作,您需要将请求正文映射到dto对象,该对象将具有名称、其他属性、parentId,然后从dto创建"Alien"对象。

a -> 新的Dto

public class 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.setName(dto.getName());
alien.setType(dto.getType());
alien.setPlanet(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(<传递父级 id 这里>); // 父级 id 可以是任何 alien id
alien.setParent(parent);
} 

您不能同时拥有parent和parent_id

Parent: 仅为其他一些外星人的父级的 Alien 实体

ParentId: 是父级的唯一 id

例如:
如何实现返回嵌套的JSON的API?

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

英文:

You need to make couple of changes to make it hierarchical, please check as below :

  1. Add extra column 'parent' to maintain hierarchy

     @JsonIgnore
    @ManyToOne(targetEntity = Alien .class, fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;parentId&quot;)
    private Alien parent;
    
  2. As mentioned by Gaurav please change private String children; as below

         @JsonIgnore
    @OneToMany(targetEntity = Alien .class, cascade = CascadeType.ALL, mappedBy = 
    &quot;parent&quot;, fetch = FetchType.LAZY)
    private List&lt;Alien&gt; children;
    
  3. As we have added one extra column to maintain hierarchy you also need to make changes
    in you POST and PUT, while creating resource you need to pass parent_id

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

Edit : DTO, normally we use to map request data.

to work above code you need map requestbody to dto object which will have name, other properties, parentId and then create "Alien" object from dto.

a -> New Dto

public class AlientDto {

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

}

b-> Controller change

 @PostMapping(&quot;/alien&quot;)
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;
} 

or

Update above code as below

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

you can not have parent and parent_id at the same time

Parent : Alien entity only which is parent to some other alien

parentId : Is a unique id for parent

for example :
如何实现返回嵌套的JSON的API?

Here we have created hierarchy
Alien1 does not have any parent
Alien1 is parent of Alien2
Alien2 is parent of Alien3 and Alien4

答案2

得分: 0

替代将子项视为字符串,将其作为对象列表处理。

用以下内容替换:

private String children;

用以下内容替代:

List<Alien> children;

并添加一个添加子项的方法:

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

假设层次结构如下:

A

|_B

|_C

然后创建:

Alien c = new Alien();
Alien b = new Alien();
Alien a = new Alien();

b.addChild(c);
a.addChild(b);
英文:

Instead of taking children as string take them as a List of object.
Replace this:

private String children;

with

List&lt;Alien&gt; children;

and add method to add child

void addChild(Alien child) {
if(this.children == null) {
this.children = new ArrayList&lt;&gt;();
}
this.children.add(child);
}

Let say Hierarchy is

A

|_B

|_C

then create:

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

huangapple
  • 本文由 发表于 2020年4月6日 20:11:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/61059553.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定