将 JsonObject 映射到 class 字段的最佳方式

Best way to map JsonObject to class fields

我正在使用:

import io.vertx.core.json.JsonObject;

假设我们有一个 class:

class Foo {
  public String barStar;
  public boolean myBool;
}

然后我们有一个像这样的 JsonObject:

var o = new JsonObject(`{"bar_star":"yes","my_bool":true}`);

是否有一些内置机制可以将 JsonObject 映射到 class 中的匹配字段?我正在考虑某种地图实例,例如:

Foo f = o.mapTo(Foo.class, Map.of("bar_star","barStar","my_bool","myBool");

所以你会传入一个地图实例,它会告诉 JsonObject 如何映射字段?能以某种方式展示如何做到这一点的例子吗?我特别询问如何在反序列化为 class.

之前映射字段

Jackson databind 文档描述了如何将 String 有效载荷转换为 POJOMapPOJO 等。查看 readValueconvertValue 中的方法 ObjectMapper.

编辑
命名约定只有一个问题。 POJO 属性不适合 JSON。您需要在 属性:

上使用 SNAKE_CASE 命名策略或 JsonProperty 注释
String json = "{\"bar_star\":\"yes\",\"my_bool\":true}";
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

JsonNode node = mapper.readTree(json);
System.out.println("Node: " + node);
System.out.println("Convert node to Foo: " + mapper.convertValue(node, Foo.class));
System.out.println("Deserialise JSON to Foo: " + mapper.readValue(json, Foo.class));

以上代码打印:

Node: {"bar_star":"yes","my_bool":true}
Convert node to Foo: Foo{barStar='yes', myBool=true}
Deserialise JSON to Foo: Foo{barStar='yes', myBool=true}

JsonProperty 你可以使用如下:

@JsonProperty("bar_star")
public String barStar;

我创建了一个小应用程序来演示这一点。
要理解所有内容,最好只看一下整个代码库。
找到它:https://github.com/thokari/epages-app-kickstart

我正在发布一些代码,这是 Whosebug 上通常需要的。
这是 Model 基础 class 和扩展它的 class:

package de.thokari.epages.app.model;

import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;

public abstract class Model {

    public static <T extends Model> T fromJsonObject(JsonObject source, Class<T> clazz) {
        return Json.decodeValue(source.encode(), clazz);
    }

    public JsonObject toJsonObject() {
        return new JsonObject(Json.encode(this));
    }

    protected static <T> T validate(final String key, final T value) {
        if (null == value) {
            throw new IllegalArgumentException(key + " must not be null");
        } else {
            return (T) value;
        }
    }

    public String toString() {
        return Json.encode(this);
    }

}
package de.thokari.epages.app.model;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

import io.vertx.core.MultiMap;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;

public class InstallationRequest extends Model {

    private static final Logger LOG = LoggerFactory.getLogger(InstallationRequest.class);

    @JsonProperty("code")
    public String code;

    @JsonProperty("api_url")
    public String apiUrl;

    @JsonProperty("access_token_url")
    public String accessTokenUrl;

    @JsonProperty("return_url")
    public String returnUrl;

    @JsonProperty("token_path")
    public String tokenPath;

    @JsonProperty("signature")
    public String signature;

    @JsonCreator
    public InstallationRequest(
        @JsonProperty("code") String code,
        @JsonProperty("api_url") String apiUrl,
        @JsonProperty("access_token_url") String accessTokenUrl,
        @JsonProperty("return_url") String returnUrl,
        @JsonProperty("signature") String signature) {

        this.code = validate("code", code);
        this.apiUrl = validate("api_url", apiUrl);
        this.accessTokenUrl = validate("access_token_url", accessTokenUrl);
        this.returnUrl = validate("return_url", returnUrl);
        this.signature = validate("signature", signature);
        try {
            this.tokenPath = accessTokenUrl.substring(apiUrl.length());
        } catch (Exception e) {
            throw new IllegalArgumentException("access_token_url must contain api_url");
        }
    }

    public static InstallationRequest fromMultiMap(MultiMap source) {
        return new InstallationRequest(
            source.get("code"), //
            source.get("api_url"), //
            source.get("access_token_url"), //
            source.get("return_url"), //
            source.get("signature"));
    }

    public static InstallationRequest fromCallbackUrl(String callbackUrl) {
        String query = callbackUrl.split("\?")[1];
        String[] parameters = query.split("&");
        String code = parameters[0].split("=")[1];

        String accessTokenUrl = parameters[1].split("=")[1];
        String urlEncodedSignature = parameters[2].split("=")[1];
        String signature = null;
        try {
            signature = URLDecoder.decode(urlEncodedSignature, "utf-8");
        } catch (UnsupportedEncodingException e) {
            LOG.error("Something went wrong because of a programming error");
        }

        // TODO why is this missing?!
        String apiUrl = accessTokenUrl.substring(0, accessTokenUrl.indexOf("/token"));

        return new InstallationRequest(code, apiUrl, accessTokenUrl, "not_needed", signature);
    }

    public Boolean hasValidSignature(String secret) {
        String algorithm = "HmacSHA256";
        String encoding = "utf-8";
        Mac mac;
        try {
            mac = Mac.getInstance(algorithm);
            mac.init(new SecretKeySpec(secret.getBytes(encoding), algorithm));
        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
            LOG.error("Signature validation failed because of programming error", e);
            return false;
        }

        byte[] rawSignature = mac.doFinal((this.code + ":" + this.accessTokenUrl).getBytes());
        String signature = Base64.getEncoder().encodeToString(rawSignature);

        return this.signature.equals(signature);
    }
}

Vert.x 有一个 mapTo 方法(参见 here)。它比建议的编码和解码解决方案更有效你已经有一个JsonObject

在后台使用 Jackson 进行映射,因此您可以简单地使用 @JsonProperty("...") 来覆盖属性的映射。

您的示例如下所示:

class Foo {
  @JsonProperty("bar_start")
  public String barStar;
  @JsonProperty("my_bool")
  public boolean myBool;
}

JsonObject obj = new JsonObject(`{"bar_star":"yes","my_bool":true}`);
Foo foo = obj.mapTo(Foo.class);