【问题标题】:Java Jersey/Jackson: return dynamic JSON propertiesJava Jersey/Jackson:返回动态 JSON 属性
【发布时间】:2020-02-20 13:10:15
【问题描述】:

我们使用 jackson 将 java 对象 (DTO) 反序列化为我们的端点的 JSON。这很好用。但是,我有一个奇怪的问题。

假设我们有一个对象用户,我们有附加到这个用户的元数据。元数据实际上可以是客户能想到的任何东西,所以这是非常动态的。

客户端可以发出请求,以检索某个用户的某些元数据属性。假设客户想要拥有以下元数据属性:age 和 favouriteTree(因为有些人有,对吧?关键是,元数据可以是任何东西,而我们事先并不知道这一点)。

我想返回以下 JSON:

{
    "userId" : 1,
    "age" : 22,
    "favouriteTree" : "Maple Tree"
}

我可以简单地拥有一个带有 int userId、int age 和 String favouriteTree 的 Java POJO。这样,我们可以让 Jackson 将其反序列化为 JSON,一切都很好。

但是,下次客户端使用同一个端点时,它可能会请求不同的属性,例如:colorEyes 和 favouriteFood,在这种情况下我想返回:

{
    "userId" : 1,
    "colorEyes" : "blue",
    "favouriteFood" : "Pizza"
}

现在我需要另一个 Java POJO 来处理这些元数据,并按照我们通常的方式对其进行反序列化。但是由于元数据可以是任何东西,我们不能这样做,因为我们不想在我们的代码中不断添加 POJO,因为不同的客户会有不同的需求。

在 Java 中是否有一种通用的方法可以做到这一点?

编辑:

我知道我可以这样做:

{
    "userId": 1,
    "metadata": [{
            "key": "colorEyes",
            "value": "blue"
        },
        {
            "key": "favouriteFood",
            "value": "Pizza"
        }
    ]
}

但如果可能的话,我想避免这种情况。

【问题讨论】:

  • 所以你想退回不同的东西却永远不知道要退回什么?
  • 我知道我会根据用户的请求返回什么,但是属性是动态的,所以我不能使用常规 POJO。

标签: java json jackson jersey


【解决方案1】:

您可以让端点返回JsonNode,这将允许您返回任何类型的 json。如果您有一些基本 DTO,可以将其映射到 JsonNode,然后在其中包含任何动态参数。

代码 sn-p 示例:

JsonNode jn = // map some BaseDTO to JsonNode
ObjectNode on = (ObjectNode)jn; // We need to cast to ObjectNode for mutation
on.put("attribute", whateverValue);

return on;

【讨论】:

【解决方案2】:

GraphQL 非常适合您的用例。

我知道这不是您要询问的技术堆栈,但它完全符合您的要求。客户端将定义他们想要从服务中获得的数据,并且只会返回请求的数据。

而且您的服务将比通过操作 json.

查看此链接了解简介:https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/

【讨论】:

  • 这实际上非常有趣,但需要对我们当前的架构进行一些修改。不过我会深入研究这个,谢谢你的帮助:)
  • @RensGroenveld 很乐意提供帮助,我们在几个项目中使用它,非常酷
  • @mart 这是“为正确的工作使用正确的工具”,而不是“为正确的工作使用酷炫的工具”。不要太迷恋一项技术,仅仅因为你想使用最新的炒作技术,就改变你的整个堆栈来解决一个简单的问题并不酷。
  • @Kayaman 我感谢您的输入,您是正确的。在您看来,graphql 不是适合此用例的工具吗? - 除了改变堆栈的事情:) 他们有他们的应用程序的上下文,他们知道是否值得改变。
  • @mart 当然不是。它违反了 KISS 原则,要求更改整个堆栈。 GraphQL 很可能有很好的用途,但你不应该仅仅因为你喜欢它就试图在任何地方硬塞它。我的解决方案要简单得多,而且很可能可以接受。你过度设计了。
【解决方案3】:

对于你说的情况,你可以完全包装你上面给出的例子,就像使用map接受请求数据,然后将map转换成json格式,可以使用

<dependency here. >
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-databind</artifactId>
       <version>2.9.9</version>
       <scope>compile</scope>
     </dependency>

这个jar中的ObjectMapper工具类,用来将地图转成json,像这样

public static String object2Json(Object o){
        try{
            return mapper.writeValueAsString(o);
        }catch (Exception e){
            log.warn("json serialize fail:", e);
            return null;
        }
    }

【讨论】:

  • 感谢您的意见。但是,我将如何填充传递的对象以及不同类型的属性?
  • 这个工具类还有一个方法可以将json格式的数据转换成你想要的任何类型的对象。我觉得可以从request中读取request参数,然后转换成map,进行相应的处理再转换。返回 json
  • 喜欢这个方法 public static T json2Object(String json, Class type){ try { return mapper.readValue(json, type); } catch (Exception e) { log.warn("json 反序列化失败:", e);返回空值; } }
  • 因为Java语言是面向对象的,如果你需要多种对象接收类型,要么使用类似map的方式来存储,要么可以提取很多对象的公共属性作为parent类,以及不同属性的单独继承。
  • 我了解到,在大型项目中,如果项目涉及到多语言系统之间的交互,可能会出现java端的类型转换问题。如果没有严格限制,可以使用map,但是为了规范,我们一般统一接口参数。格式化,或使用 if 来确定每个不同的对象类型
猜你喜欢
  • 2012-01-23
  • 2013-03-24
  • 2019-10-08
  • 2015-03-10
  • 1970-01-01
  • 2012-08-21
  • 1970-01-01
  • 2017-12-24
相关资源
最近更新 更多