Json 到 Java 与杰克逊的 POJO API 视频群聊

Json to Java POJO with Jackson API Hangaouts

我在网上找不到任何帮助,所以我在这里问。我想用 java 类 创建一张 Google 卡片。所以,我要创建的 JSON 是:

{
   "thread":{
      "name":"some url here"
   },
   "cards":[
      {
         "sections":[
            {
               "widgets":[
                  {
                     "textParagraph":{
                        "text":"bla bla"
                     }
                  },
                  {
                     "buttons":[
                        {
                           "textButton":{
                              "text":"reminder in 10",
                              "onClick":{
                                 "openLink":{
                                    "url":"some Method"
                                 }
                              }
                           }
                        }
                     ]
                  }
               ]
            }
         ]
      }
   ]
}

我已经尝试了几种方法。我什至发现了一个生成 POJO 的站点,但我无法理解它。我的尝试是这样的:

 Cards cards = new Cards();
 Sections sections = new Sections();
 Widgets widgets = new Widgets();
 TextParagraph textParagraph = new TextParagraph();
 Text text = new Text();
 Buttons button = new Buttons();
 TextButton textButton = new TextButton();
 OnClick onClick = new OnClick();
 OpenLink openLink = new OpenLink();
 NoNameClass haha = new NoNameClass();
 ThreadX threadx = new ThreadX();

 threadx.name = "spaces/" + reminder.getSpaceId() + "/threads/" + reminder.getThreadId();
 text.text = "<" + reminder.getSenderDisplayName() + "> " + reminder.getWhat();
 openLink.url = "https://media0.giphy.com/media/QNnKbtl03OGsM/giphy.gif?cid=3640f6095c5851de3064736a2ef2345a";
 onClick.openLink = openLink;
 textButton.onClick = onClick;
 textButton.text = "se 10";
 haha.textButton = textButton;
 textParagraph.textParagraph = text;
 button.buttons = Lists.newArrayList(haha);
 widgets.widgets = Lists.newArrayList(textParagraph, button);
 sections.sections = Lists.newArrayList(widgets);
 cards.thread = threadx;
 cards.cards = Lists.newArrayList(sections);

为了发送该请求,我创建了一个 JsonHttpContent

private String send(GenericUrl url, Cards message, String httpMethod) {
    HttpContent content = new JsonHttpContent(new JacksonFactory(),message);

    HttpRequest request;
    try {
        if (httpMethod.equals("POST")) {
            request = requestFactory.buildPostRequest(url, content);
        } else {
            request = requestFactory.buildGetRequest(url);
        }
    } catch (Exception e) {
        logger.error("Error creating request using url: {}", url, e);
        return null;
    }

    String response = "";
    try {
        HttpResponse httpResponse = request.execute();
        response = httpResponse.parseAsString();
    } catch (IOException e) {
        logger.error("Error creating request using url: {}", url, e);
    }

    return response;
}

但这根本不起作用,我 JsonIgnoreProperties 为真 但我得到了那个错误

{
   "error":{
      "code":400,
      "message":"Message cannot be empty. Discarding empty create message request in spaces/AAAADvB8eGY.",
      "errors":[
         {
            "message":"Message cannot be empty. Discarding empty create message request in spaces/AAAADvB8eGY.",
            "domain":"global",
            "reason":"badRequest"
         }
      ],
      "status":"INVALID_ARGUMENT"
   }
}

我已经设置了很多参数,但没有读取任何内容,所以我需要帮助。

Google API一般不太容易构建结构化数据模型。在 Card Formatting Messages page we can see many different JSON payloads for different types of cards. On Github we can find Hangouts Chat code samples projects which introduces CardResponseBuilder class 它提供了一些构建器方法来构建不同类型的卡片。我认为,这是一个很好的方法。如果可以,你可以试试这个class。它依赖于 javax.json 库。使用这个 class 并且知道 Jackson 序列化 MapJSON ObjectListJSON Array 我们可以创建非常相似的 class:

class CardResponseBuilder {

    private interface Builder {
        Object get();
    }

    private ObjectBuilder createObjectBuilder() {
        return new ObjectBuilder();
    }

    private static class ObjectBuilder implements Builder {
        Map<String, Object> map = new HashMap<>(5);

        ObjectBuilder add(String key, Object value) {
            map.put(key, value);
            return this;
        }

        ObjectBuilder add(String key, Builder builder) {
            return add(key, builder.get());
        }

        @Override
        public Map<String, Object> get() {
            return map;
        }
    }

    private ArrayBuilder createArrayBuilder() {
        return new ArrayBuilder();
    }

    private static class ArrayBuilder implements Builder {
        List<Object> list = new ArrayList<>(4);

        ArrayBuilder add(Builder builder) {
            list.add(builder.get());
            return this;
        }

        @Override
        public List<Object> get() {
            return list;
        }
    }

    public static final String UPDATE_MESSAGE = "UPDATE_MESSAGE";
    public static final String NEW_MESSAGE = "NEW_MESSAGE";

    private ObjectBuilder headerNode;
    private ObjectBuilder responseNode;
    private ArrayBuilder widgetsArray;
    private ArrayBuilder cardsArray;

    /**
     * Default public constructor.
     */
    public CardResponseBuilder() {
        this.responseNode = createObjectBuilder();
        this.cardsArray = createArrayBuilder();
        this.widgetsArray = createArrayBuilder();
    }

    /**
     * Creates a new CardResponseBuilder object for responding to an interactive card click.
     *
     * @param updateType the update type, either UPDATE_MESSAGE or NEW_MESSAGE.
     */
    public CardResponseBuilder(String updateType) {
        this();
        responseNode.add("actionResponse",
                createObjectBuilder().add("type", updateType));
    }

    /**
     * Adds a header to the card response.
     *
     * @param title    the header title
     * @param subtitle the header subtitle
     * @param imageUrl the header image
     * @return this CardResponseBuilder
     */
    public CardResponseBuilder header(String title, String subtitle, String imageUrl) {
        this.headerNode = createObjectBuilder()
                .add("header", createObjectBuilder()
                        .add("title", title)
                        .add("subtitle", subtitle)
                        .add("imageUrl", imageUrl)
                        .add("imageStyle", "IMAGE"));
        return this;
    }

    /**
     * Adds a TextParagraph widget to the card response.
     *
     * @param message the message in the text paragraph
     * @return this CardResponseBuilder
     */
    public CardResponseBuilder textParagraph(String message) {
        this.widgetsArray.add(
                createObjectBuilder()
                        .add("textParagraph",
                                createObjectBuilder().add("text", message)));
        return this;
    }

    /**
     * Adds a KeyValue widget to the card response.
     * <p>
     * For a list of icons that can be used, see:
     * https://developers.google.com/hangouts/chat/reference/message-formats/cards#builtinicons
     *
     * @param key         the key or top label
     * @param value       the value or content
     * @param bottomLabel the content below the key/value pair
     * @param iconName    a specific icon
     * @return this CardResponseBuilder
     */
    public CardResponseBuilder keyValue(String key, String value,
                                        String bottomLabel, String iconName) {
        this.widgetsArray.add(createObjectBuilder()
                .add("keyValue", createObjectBuilder()
                        .add("topLabel", key)
                        .add("content", value)
                        .add("bottomLabel", bottomLabel)
                        .add("icon", iconName)));
        return this;
    }

    /**
     * Adds an Image widget to the card response.
     *
     * @param imageUrl    the URL of the image to display
     * @param redirectUrl the URL to open when the image is clicked.
     * @return this CardResponseBuilder
     */
    public CardResponseBuilder image(String imageUrl, String redirectUrl) {
        this.widgetsArray.add(createObjectBuilder()
                .add("image", createObjectBuilder()
                        .add("imageUrl", imageUrl)
                        .add("onClick", createObjectBuilder()
                                .add("openLink", createObjectBuilder()
                                        .add("url", redirectUrl)))));
        return this;
    }

    /**
     * Adds a Text Button widget to the card response.
     * <p>
     * When clicked, the button opens a link in the user's browser.
     *
     * @param text        the text on the button
     * @param redirectUrl the link to open
     * @return this CardResponseBuilder
     */
    public CardResponseBuilder textButton(String text, String redirectUrl) {
        this.widgetsArray.add(createObjectBuilder()
                .add("buttons", createArrayBuilder()
                        .add(createObjectBuilder()
                                .add("textButton", createObjectBuilder()
                                        .add("text", text)
                                        .add("onClick", createObjectBuilder()
                                                .add("openLink", createObjectBuilder()
                                                        .add("url", redirectUrl)))))));
        return this;
    }

    /**
     * Adds an Image Button widget to the card response.
     * <p>
     * When clicked, the button opens a link in the user's browser.
     *
     * @param iconName    the icon to display
     * @param redirectUrl the link to open
     * @return this CardResponseBuilder
     */
    public CardResponseBuilder imageButton(String iconName, String redirectUrl) {
        this.widgetsArray.add(createObjectBuilder()
                .add("buttons", createArrayBuilder()
                        .add(createObjectBuilder()
                                .add("imageButton", createObjectBuilder()
                                        .add("icon", iconName)
                                        .add("onClick", createObjectBuilder()
                                                .add("openLink", createObjectBuilder()
                                                        .add("url", redirectUrl)))))));
        return this;
    }

    /**
     * Adds an interactive Text Button widget to the card response.
     * <p>
     * When clicked, the button sends a new request to the bot, passing along the custom actionName
     * and parameter values. The actionName and parameter values are defined by the developer when the
     * widget is first declared (as shown below).
     *
     * @param text                   the text to display
     * @param actionName             the custom action name
     * @param customActionParameters the custom key value pairs
     * @return this CardResponseBuilder
     */
    public CardResponseBuilder interactiveTextButton(String text, String actionName,
                                                     Map<String, String> customActionParameters) {

        // Define the custom action name and parameters for the interactive button.
        ObjectBuilder actionNode = createObjectBuilder()
                .add("actionMethodName", actionName);

        if (customActionParameters != null && customActionParameters.size() > 0) {
            addCustomActionParameters(actionNode, customActionParameters);
        }

        this.widgetsArray.add(createObjectBuilder()
                .add("buttons", createArrayBuilder()
                        .add(createObjectBuilder()
                                .add("textButton", createObjectBuilder()
                                        .add("text", text)
                                        .add("onClick", createObjectBuilder()
                                                .add("action", actionNode))))));
        return this;
    }

    /**
     * Adds an interactive Image Button widget to the card response.
     * <p>
     * When clicked, the button sends a new request to the bot, passing along the custom actionName
     * and parameter values. The actionName and parameter values are defined by the developer when the
     * widget is first declared (as shown below).
     *
     * @param iconName               the pre-defined icon to display.
     * @param actionName             the custom action name
     * @param customActionParameters the custom key value pairs
     * @return this CardResponseBuilder
     */
    public CardResponseBuilder interactiveImageButton(String iconName, String actionName,
                                                      Map<String, String> customActionParameters) {

        // Define the custom action name and parameters for the interactive button.
        ObjectBuilder actionNode = createObjectBuilder()
                .add("actionMethodName", actionName);

        if (customActionParameters != null && customActionParameters.size() > 0) {
            addCustomActionParameters(actionNode, customActionParameters);
        }

        this.widgetsArray.add(createObjectBuilder()
                .add("buttons", createArrayBuilder()
                        .add(createObjectBuilder()
                                .add("imageButton", createObjectBuilder()
                                        .add("icon", iconName)
                                        .add("onClick", createObjectBuilder()
                                                .add("action", actionNode))))));
        return this;
    }

    /**
     * Builds the card response and returns a JSON object node.
     *
     * @return card response as JSON-formatted string
     */
    public Object build() {

        // If you want your header to appear before all other cards,
        // you must add it to the `cards` array as the first / 0th item.
        if (this.headerNode != null) {
            this.cardsArray.add(this.headerNode);
        }

        return responseNode.add("cards", this.cardsArray
                .add(createObjectBuilder()
                        .add("sections", createArrayBuilder()
                                .add(createObjectBuilder()
                                        .add("widgets", this.widgetsArray)))))
                .get();
    }

    /**
     * Applies sets of custom parameters to the parameter field of an action.
     *
     * @param actionNode             the JSON action node
     * @param customActionParameters the parameters to apply to the custom action
     */
    private void addCustomActionParameters(ObjectBuilder actionNode,
                                           Map<String, String> customActionParameters) {
        ArrayBuilder parametersArray = createArrayBuilder();

        customActionParameters.forEach((k, v) -> {
            parametersArray.add(createObjectBuilder()
                    .add("key", k)
                    .add("value", v));
        });

        actionNode.add("parameters", parametersArray);
    }
}

我只是根据 MapJsonObjectBuilder 替换为 ObjectBuilder,将 JsonArrayBuilder 替换为基于 ListArrayBuilder。下面是该构建器的简单用法:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        Object response = new CardResponseBuilder()
                .textParagraph("bla bla")
                .textButton("reminder in 10", "some Method")
                .build();
        System.out.println(mapper.writeValueAsString(response));
    }
}

以上代码打印:

{
  "cards" : [ {
    "sections" : [ {
      "widgets" : [ {
        "textParagraph" : {
          "text" : "bla bla"
        }
      }, {
        "buttons" : [ {
          "textButton" : {
            "onClick" : {
              "openLink" : {
                "url" : "some Method"
              }
            },
            "text" : "reminder in 10"
          }
        } ]
      } ]
    } ]
  } ]
}

我没有添加处理 thread 属性 的方法,因为我没有在文档中找到它,但您应该可以自己完成。