Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/webpack/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java DTO中的动态字段类型_Java_Spring_Spring Mvc_Jackson - Fatal编程技术网

Java DTO中的动态字段类型

Java DTO中的动态字段类型,java,spring,spring-mvc,jackson,Java,Spring,Spring Mvc,Jackson,我使用的是springmvc,我有一个如下结构的DTO,从客户机(一个foo实体)接收JSON数据,并将其保存到数据库中JPA: public class FooDTO { public Integer id; public String label; public Double amount; public List<Integer> states; ... 使用SimpleDto public class SimpleDto {

我使用的是
springmvc
,我有一个如下结构的DTO,从客户机(一个
foo
实体)接收
JSON
数据,并将其保存到数据库中
JPA

public class FooDTO {

    public Integer id;
    public String label;
    public Double amount;
    public List<Integer> states;
    ...
使用
SimpleDto

public class SimpleDto {
    public Integer value;
    public String label;
}
区别在于
状态
类型有时是
列表
,有时是
列表
,我不想创建另一个dto

那么,如何在dto(json)中实现动态字段类型呢


p.S JSON数据由
com.fasterxml.jackson.core

为DTO类型使用Spring类型转换器处理。这样,客户端可以发布stateId,转换器将解析给定ID的正确DTO类型


这里有一个例子:

您可以使用
泛型
,将
列表状态
更改为
列表状态
我建议您使用不同的类:FooInfoDTO、foodailsdto。它通常在您拥有主详细信息表单时使用。在master(表)中,您显示有关对象的简短信息(一个DTO),然后导航到详细信息,您将获取完整的对象数据(另一个DTO)

我建议不要添加另一个促进重复的DTO。 但是,您仍然需要添加另一个DTO,该DTO将专用于您各自的服务。您只需使用层次结构定义DTO

public class FooDTO {

    public Integer id;
    public String label;
    public Double amount;
}
定义您的响应DTO以提供详细信息,方法是将通用详细信息DTO扩展为FooDTO,如下所示:

public class FooDetailsOutDTO extends FooDTO {

    public List<Integer> states;

}
public class FooUpdateDetailsInDTO extends FooDTO {

     public List<SimpleDto> states;

}
公共类foodDetailsOutTo将FooDTO扩展为{
公开名单国家;
}
对于“编辑”,定义DTO如下:

public class FooDetailsOutDTO extends FooDTO {

    public List<Integer> states;

}
public class FooUpdateDetailsInDTO extends FooDTO {

     public List<SimpleDto> states;

}
public类foodupdatedetailsindto扩展FooDTO{
公开名单国家;
}
您可以为item
POJO
使用注释和两个构造函数。如果数组
1-arg中存在基元,则将使用构造函数。在完全设置对象的情况下,将使用构造函数。请参见以下示例:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        String json = "{\"id\":1,\"label\":\"LABEL\",\"amount\":1.23,\"states\":[1,{\"value\":2},{\"value\":3,\"label\":\"LAB\"}]}";
        ObjectMapper mapper = new ObjectMapper();

        Foo foo = mapper.readValue(json, Foo.class);
        System.out.println(foo);
    }
}

class Foo {

    private Integer id;
    private String label;
    private Double amount;
    private List<State> states;

    // getters, setters, toString
}

class State {

    private Integer value;
    private String label;

    @JsonCreator(mode = Mode.DELEGATING)
    public State(@JsonProperty("value") Integer value) {
        this(value, null);
    }

    @JsonCreator
    public State(@JsonProperty("value") Integer value, @JsonProperty("label") String label) {
        this.value = value;
        this.label = label;
    }

    // getters, setters, toString
}
使用的版本:

写入dto

public class FooDTO {

    public Integer id;
    public String label;
    public Double amount;
    public List<Object> states;
}
public class FooDTO{
公共整数id;
公共字符串标签;
公共双倍金额;
公开名单国家;
}

在服务类中键入DTO并处理异常

另一种重新建模方法:

 public class FooDTO {

    public Integer id;
    public String label;
    public Double amount;
    //not null!
    public List<Integer> states;
    //nullable!!
    ... List<String> stateLabels;
   // you should ensure "stable/consistent index/ordering" (relative to states)
   ...
public class FooDTO{
公共整数id;
公共字符串标签;
公共双倍金额;
//不是空的!
公开名单国家;
//可为空!!
…列出国家标签;
//您应该确保“稳定/一致的索引/顺序”(相对于状态)
...
…并相应地将其用于“get”(单独访问标签)和“post”(省略标签;)

------------------------------------

更好的是:

   Map<Integer, String> states; // !?
映射状态;/!?

您只需添加一个getter,它在
SimpleDto中返回整数

使用简单java流添加一个getter,该getter在
FooDTO
中返回
List
,并使用DTO getter映射到整数

states.stream().map(SimpleDto::getValue).collect(Collectors.toList());

使用自定义反序列化程序是解决问题的一种方法

    public class DynamicDeserializer extends JsonDeserializer {
    @Override
    public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String requestString = jp.readValueAsTree().toString();
        JSONArray jo = new JSONArray(requestString);
        List<SimpleDto> simpleDtoList = new ArrayList<>();
        List<Integer> integers = new ArrayList<>();
        if(jo!=null && jo.length()>0) {
            for (int i = 0; i < jo.length(); i++) {
                Object string = jo.get(0);
                if(string!=null && string instanceof JSONObject){
                    JSONObject value = jo.getJSONObject(i);
                    SimpleDto simpleDto = new SimpleDto();
                    simpleDto.setValue(value.getInt("value"));
                    simpleDtoList.add(simpleDto);
                }else{
                    integers.add(jo.getInt(0));
                }
            }
        }


        return integers.isEmpty() ? simpleDtoList:integers;
    }
}
整数列表的输入有效负载
您可以尝试重新设计整个体系结构。使用相关集合拆分主实体

为每个实体提供独立的服务来添加/删除/设置状态。通过这种方式,您可以轻松地为您的客户端提供REST服务,这将是明确的用法

下面是一组可能的方法,通过REST接口实现:

@Path(../foo)
@Produces
public interface FooService {
  //CRUD methods on Foo itself which work with attributes of Foo only
  ...
  @GET
  @Path("/{fooId}")
  FooDTO findById(@PathParam("fooId") int fooId);

  //status-related methods:
  @GET
  @Path("/{fooId}/status")
  List<SimpleDto> statuses(@PathParam("fooId") int fooId);

  @Post
  @Path("/{fooId}/status")
  void addStatus(@PathParam("fooId") int fooId, int statusId);

  @DELETE
  @Path("{fooId}/status")
  void deleteStatus(@PathParam("fooId") int fooId, int statusId);

  @PUT
  @Path("/status")
  void setStatuses(@PathParam("fooId") int fooId, List<Integer> newStatuses);
}
@Path(../foo)
@产生
公共接口服务{
//Foo本身上的CRUD方法,这些方法只处理Foo的属性
...
@得到
@路径(“/{fooId}”)
foodtofindbyid(@PathParam(“fooId”)intfooid);
//与状态相关的方法:
@得到
@路径(“/{fooId}/status”)
列出状态(@PathParam(“fooId”)int fooId);
@职位
@路径(“/{fooId}/status”)
void addStatus(@PathParam(“fooId”)int fooId,int statusId);
@删除
@路径(“{fooId}/status”)
void deleteStatus(@PathParam(“fooId”)int-fooId,int-statusId);
@放
@路径(“/状态”)
void setstatus(@PathParam(“fooId”)int fooId,List newstatus);
}
对于此解决方案,还有一些备选方案,我更愿意返回:

  @GET
  @Path("/{fooId}/status")
  List<Integer> statuses(@PathParam("fooId") int fooId);
@GET
@路径(“/{fooId}/status”)
列出状态(@PathParam(“fooId”)int fooId);
而不是DTO的列表。然后将提供一个服务来获取所有状态及其名称,而无需连接到Foo:

public interface StatusService {
  List<SimpleDto> statuses();
}
公共接口状态服务{
列出状态();
}

为了简化GUI组件的实现,您可以创建Rest服务,该服务将返回一个组合数据,就像在第二个FooDto版本中一样。它还将减少Rest调用的数量。但是,使用单独的方法直接处理一组项会有很大帮助。

我不想使用额外的DTOI我不想使用空标签(null)@Youssef,这只是
toString
方法实现,它以这种方式显示这些对象。Label属性设置为
null
而不是
“null”
String
。请尝试我的解决方案,您会看到的。我知道它有效,我在发布此问题之前尝试过它……但我正在寻找一个具有真正动态字段的更好的解决方案type@Youssef,您所说的
实动态字段类型
是什么意思?您想如何区分
整数
s和
DT的列表O
?您可以创建
列表
,一旦接收到
整数
,对象将成为
映射
。无法为
整数
DTO
动态映射它。您需要为此字段编写自定义反序列化程序。我的解决方案在这两种情况下为您提供相同的类型,您甚至可以混合使用pr使用
DTO
进行模拟。我不想使用额外的DTO我不明白。只需将其设置为
对象
并使用一个简单的
实例
,为什么这么复杂?@Eugene yeah
实例
用于基本类型,但对于我的
实例
,我想我必须使用@manojramana?你知道什么
实例吗是吗?你说你有
{
  "states": [
      1,2
  ]
}
@Path(../foo)
@Produces
public interface FooService {
  //CRUD methods on Foo itself which work with attributes of Foo only
  ...
  @GET
  @Path("/{fooId}")
  FooDTO findById(@PathParam("fooId") int fooId);

  //status-related methods:
  @GET
  @Path("/{fooId}/status")
  List<SimpleDto> statuses(@PathParam("fooId") int fooId);

  @Post
  @Path("/{fooId}/status")
  void addStatus(@PathParam("fooId") int fooId, int statusId);

  @DELETE
  @Path("{fooId}/status")
  void deleteStatus(@PathParam("fooId") int fooId, int statusId);

  @PUT
  @Path("/status")
  void setStatuses(@PathParam("fooId") int fooId, List<Integer> newStatuses);
}
  @GET
  @Path("/{fooId}/status")
  List<Integer> statuses(@PathParam("fooId") int fooId);
public interface StatusService {
  List<SimpleDto> statuses();
}