【问题标题】:Parse POST body to java object using spark使用 spark 将 POST 正文解析为 java 对象
【发布时间】:2017-09-17 01:10:31
【问题描述】:

我之前从 spring 迁移到 spark,现在我被困在一些基本的东西上。

当我发出 POST 请求在正文中发送数据时,我希望将 JAVA 对象返回到控制器中......

春天我曾经这样做

@RequestBody User user

它是自动“填充”的..

现在有了 spark 我有方法了:

request.body();

但这给了我一个像这样的序列化字符串:

id=7&name=Pablo+Mat%C3%ADas&lastname=Gomez&githubUsername=pablomatiasgomez

那么我怎样才能得到用户 DTO 呢?

当然,User类是有属性的

  • 身份证
  • 姓名
  • 姓氏
  • github用户名

【问题讨论】:

  • 您确定您选择了正确的标签“Apache Spark”spark.apache.org 吗?
  • @maasg 对不起,我现在看到 spark 没有标签
  • 我认为还没有这样的功能。尽量不要在文档中使用request.params("id"); ...
  • @JorgeCampos 但是参数是空的,因为 POST 方法发送正文中的数据。刚查了一下,数据只在body里,不在request.params()

标签: java spring spark-java


【解决方案1】:

AFAIK,Spark 不提供此功能。当我将它用于一个小型宠物项目时,我编写了一些小的实用方法来将 URL 编码的字符串解析为 POJO,如下所示:

import com.google.gson.Gson;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.LinkedHashMap;
import java.util.Map;

public class Test {

  private static final Gson GSON = new Gson();

  public static <T> T convert(String urlencoded, Class<T> type) {
    try {
      Map<String, Object> map = asMap(urlencoded);
      String json = GSON.toJson(map);
      return GSON.fromJson(json, type);
    }
    catch (Exception e) {
      e.printStackTrace(); // TODO log
      return null;
    }
  }

  public static Map<String, Object> asMap(String urlencoded) throws UnsupportedEncodingException {
    return asMap(urlencoded, "UTF-8");
  }

  @SuppressWarnings("unchecked")
  public static Map<String, Object> asMap(String urlencoded, String encoding) throws UnsupportedEncodingException {

    Map<String, Object> map = new LinkedHashMap<>();

    for (String keyValue : urlencoded.trim().split("&")) {

      String[] tokens = keyValue.trim().split("=");
      String key = tokens[0];
      String value = tokens.length == 1 ? null : URLDecoder.decode(tokens[1], encoding);

      String[] keys = key.split("\\.");
      Map<String, Object> pointer = map;

      for (int i = 0; i < keys.length - 1; i++) {

        String currentKey = keys[i];
        Map<String, Object> nested = (Map<String, Object>) pointer.get(keys[i]);

        if (nested == null) {
          nested = new LinkedHashMap<>();
        }

        pointer.put(currentKey, nested);
        pointer = nested;
      }

      pointer.put(keys[keys.length - 1], value);
    }

    return map;
  }

  public static void main(String[] args) {
    String payload = "id=7&name=Pablo+Mat%C3%ADas&lastname=Gomez&githubUsername=pablomatiasgomez";
    User user = convert(payload, User.class);
    System.out.println(user);
  }
}

class User {

  long id;
  String name;
  String lastname;
  String githubUsername;

  @Override
  public String toString() {
    return "User{" +
        "id=" + id +
        ", name='" + name + '\'' +
        ", lastname='" + lastname + '\'' +
        ", githubUsername='" + githubUsername + '\'' +
        '}';
  }
}

运行此Test 类将在您的控制台上打印以下内容:

用户{id=7, name='Pablo Matías', lastname='Gomez', githubUsername='pablomatiasgomez'}

请注意,当User 中包含嵌套结构时,这也有效,例如,Address 由几个其他字段组成。只需像这样用"." 分隔字段即可:

public class Test {

  // ... same code ...

  public static void main(String[] args) {
    String payload = "id=7&name=Pablo+Mat%C3%ADas&lastname=Gomez&githubUsername=pablomatiasgomez&" +
        "address.street=Coolsingel&address.number=42a&address.city=Rotterdam";
    User user = convert(payload, User.class);
    System.out.println(user);
  }
}

class User {

  long id;
  String name;
  String lastname;
  String githubUsername;
  Address address;

  @Override
  public String toString() {
    return "User{" +
        "\n  id=" + id +
        "\n  name='" + name + '\'' +
        "\n  lastname='" + lastname + '\'' +
        "\n  githubUsername='" + githubUsername + "'" +
        "\n  address=" + address + "\n" +
        '}';
  }
}

class Address {


  String street;
  String number;
  String city;

  @Override
  public String toString() {
    return "Address{" +
        "street='" + street + '\'' +
        ", number='" + number + '\'' +
        ", city='" + city + '\'' +
        '}';
  }
}

将打印:

用户{
  id=7
  名称='巴勃罗·马蒂亚斯'
  姓氏='戈麦斯'
  github用户名='pablomatiasgomez'
  地址=地址{街道='Coolsingel',号码='42a',城市='鹿特丹'}
}

编辑

如果负载包含一个列表,比如Users,你可以这样做:

public class Test {

  private static final Gson GSON = new Gson();

  public static <T> T convert(String urlencoded, Type type) {
    try {
      Map<String, Object> map = asMap(urlencoded);
      String json = GSON.toJson(containsList(map) ? map.values() : map);
      return GSON.fromJson(json, type);
    }
    catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  private static boolean containsList(Map<String, Object> map) {
    return !map.isEmpty() && new ArrayList<>(map.keySet()).get(0).contains("[");
  }

  public static Map<String, Object> asMap(String urlencoded) throws UnsupportedEncodingException {
    return asMap(urlencoded, "UTF-8");
  }

  @SuppressWarnings("unchecked")
  public static Map<String, Object> asMap(String urlencoded, String encoding) throws UnsupportedEncodingException {

    Map<String, Object> map = new LinkedHashMap<>();

    for (String keyValue : urlencoded.trim().split("&")) {

      String[] tokens = keyValue.trim().split("=");
      String key = tokens[0];
      String value = tokens.length == 1 ? null : URLDecoder.decode(tokens[1], encoding);

      String[] keys = key.split("\\.");
      Map<String, Object> pointer = map;

      for (int i = 0; i < keys.length - 1; i++) {

        String currentKey = keys[i];
        Map<String, Object> nested = (Map<String, Object>) pointer.get(keys[i]);

        if (nested == null) {
          nested = new LinkedHashMap<>();
        }

        pointer.put(currentKey, nested);
        pointer = nested;
      }

      pointer.put(keys[keys.length - 1], value);
    }

    return map;
  }

  public static void main(String[] args) throws Exception {

    String payload = "id=7&name=Pablo Mat%C3%ADas";
    User user = convert(payload, User.class);
    System.out.println("single user   -> " + user);

    payload = "users[0].id=7&users[0].name=Pablo Mat%C3%ADas&users[1].id=42&users[1].name=Bart";
    List<User> users = convert(payload, new TypeToken<List<User>>(){}.getType());
    System.out.println("list of users -> : " + users);
  }
}

class User {

  long id;
  String name;

  @Override
  public String toString() {
    return "User{" +
        "id=" + id +
        ", name='" + name + '\'' +
        '}';
  }
}

将打印:

单用户 -> 用户{id=7, name='Pablo Matías'}
用户列表 -> : [User{id=7, name='Pablo Matías'}, User{id=42, name='Bart'}]

【讨论】:

  • 不错的方法,如果 spark 没有内置任何东西,那么这可能是最好的解决方案。谢谢
  • 你知道有没有办法用列表来做这个?例如,一个键是:example[0].id?
  • @PabloMatíasGomez,检查我的编辑
【解决方案2】:

我找到了一种不涉及 URL 编码的更简单的方法。

在客户端,将您的 javascript 对象转换为 JSON 字符串并使用它设置查询参数 (yourObject):

var obj = null;
obj = {
  yourObject: JSON.stringify(currentObject)
};
   
$.ajax({
  type: "GET",
  url: "saveAnObject",
  data: obj, 
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function(data) {
    console.log('saveAnObject result: ' + data + ".");

  },
  error: function() {
    
  },
  cache: false
});

然后在 Spark 中:

 get("/saveAnObject", (req, res) - > {
     
     String yourObjectStr = "" + req.queryParams("yourObject");
   
     // Convert the JSON string to a POJO obj
     Gson gson = new GsonBuilder().create();
     YourObject pojoObj = gson.fromJson(yourObjectStr , YourObject.class);

     // do something with your pojoObj object.
   

【讨论】:

  • 我为什么要这样做?如果我要制作一个 ajax,那么我会将正文中的 json 作为内容类型 json 发送,但事实并非如此。我说的是提交的表单,它们以这种方式发送序列化的数据,而不是 json 格式。
  • 使用此方法,您可以在一个 Ajax GET 中传递多个类类型的多个对象。因此,您可以发送一个 Account 对象以及您的 User 对象。我发现 req.queryParams("yourObject") 便于解析这些对象。我倾向于不使用表单提交,您最初的问题没有提到表单提交。
  • 是的,所以这是常识解决方案,但您不必包装整个对象,请求的正文可以是json。因此,不要执行前 4 行代码,只需将 data: obj 更改为 data: JSON.stringify(currentObject)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-17
  • 2016-02-17
  • 2016-12-02
相关资源
最近更新 更多