如何将 jackson 中的 @JsonAnySetter 转换为 circe

how to convert @JsonAnySetter in jackson to circe

我正在重写一些 java 代码,这些代码使用 jackson json 解析为 scala circe。

javaDeviceclass是这个-

package forjava;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({"ua", "dnt", "ip", "devicetype"})
public class Device implements Serializable {
    @JsonProperty("ua")
    private String ua;
    @JsonProperty("dnt")
    private Integer dnt;
    @JsonProperty("ip")
    private String ip;
    @JsonProperty("devicetype")
    private Integer devicetype;

    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap();

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

    private static final long serialVersionUID = -4938649324295079141L;

    public Device() {
    }

    public Device(String ua, Integer dnt, Integer lmt, String ip, String ipv6, Integer devicetype, String make) {
        this.ua = ua;
        this.dnt = dnt;
        this.ip = ip;
        this.devicetype = devicetype;
    }

    @JsonProperty("ua")
    public String getUa() {
        return this.ua;
    }

    @JsonProperty("ua")
    public void setUa(String ua) {
        this.ua = ua;
    }

    @JsonProperty("dnt")
    public Integer getDnt() {
        return this.dnt;
    }

    @JsonProperty("dnt")
    public void setDnt(Integer dnt) {
        this.dnt = dnt;
    }

    @JsonProperty("ip")
    public String getIp() {
        return this.ip;
    }

    @JsonProperty("ip")
    public void setIp(String ip) {
        this.ip = ip;
    }

    @JsonProperty("devicetype")
    public Integer getDevicetype() {
        return this.devicetype;
    }

    @JsonProperty("devicetype")
    public void setDevicetype(Integer devicetype) {
        this.devicetype = devicetype;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(Device.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('[');
        sb.append("ua");
        sb.append('=');
        sb.append(this.ua == null ? "<null>" : this.ua);
        sb.append(',');
        sb.append("dnt");
        sb.append('=');
        sb.append(this.dnt == null ? "<null>" : this.dnt);
        sb.append(',');
        sb.append("ip");
        sb.append('=');
        sb.append(this.ip == null ? "<null>" : this.ip);
        sb.append(',');
        sb.append("devicetype");
        sb.append('=');
        sb.append(this.devicetype == null ? "<null>" : this.devicetype);
        sb.append(',');
        sb.append("additionalProperties");
        sb.append('=');
        sb.append(this.additionalProperties == null ? "<null>" : this.additionalProperties);
        sb.append(',');
        if (sb.charAt(sb.length() - 1) == ',') {
            sb.setCharAt(sb.length() - 1, ']');
        } else {
            sb.append(']');
        }
        return sb.toString();
    }

    public int hashCode() {
        int result = 1;

        result = result * 31 + (this.ua == null ? 0 : this.ua.hashCode());
        result = result * 31 + (this.devicetype == null ? 0 : this.devicetype.hashCode());
        result = result * 31 + (this.ip == null ? 0 : this.ip.hashCode());
        result = result * 31 + (this.dnt == null ? 0 : this.dnt.hashCode());
        return result;
    }

    public boolean equals(Object other) {
        if (other == this) {
            return true;
        } else if (!(other instanceof Device)) {
            return false;
        } else {
            Device rhs = (Device)other;
            return (Objects.equals(this.ua, rhs.ua))
                    && (Objects.equals(this.devicetype, rhs.devicetype) || this.devicetype != null
                    && this.devicetype.equals(rhs.devicetype))
                    && (Objects.equals(this.ip, rhs.ip))
                    && (Objects.equals(this.dnt, rhs.dnt)) ;
        }
    }
}

下面是解析 json -

的 java 代码
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class DeviceJsonParserDemo {
    public static void main(String[] args) throws IOException {

        ObjectMapper jsonMapperBidRequest  = new ObjectMapper();
        String dev = Files.readString(Paths.get("src/main/resources/device.json"));
        Device device = jsonMapperBidRequest.readValue(dev, Device.class);
        System.out.println(device);
    }
}

当你运行上面的代码你会得到下面的输出-

Device@5876a9af[ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36,dnt=<null>,ip=<null>,devicetype=<null>,additionalProperties={started=now, devtime=now}]

正如您在上面的输出中注意到的,不在 Device class 中的字段在 additionalProperties 字段中。

现在我想把上面的逻辑迁移到typelevel circe

我已解码

import io.circe.{Decoder, HCursor}

case class Device (ua: String, dnt: Option[Int], ip: Option[String], devicetype: Option[Int], addlProperties: Map[String, Any] = Map.empty)

object Device {
  implicit val decodeFoo: Decoder[Device] = new Decoder[Device] {
    final def apply(c: HCursor): Decoder.Result[Device] =
      for {
        ua <- c.downField("ua").as[String]
        dnt <- c.downField("dnt").as[Option[Int]]
        ip <- c.downField("ip").as[Option[String]]
        devicetype <- c.downField("devicetype").as[Option[Int]]
      } yield new Device(ua, dnt, ip, devicetype)
  }
}

java解析逻辑为

import scala.io.Source
import io.circe.parser._

object DeviceJsonCirceDecodeDemo extends App {
  val devString = Source.fromFile("src/main/resources/device.json").mkString
  val device = decode[Device](devString)
  println(device)
}

当我 运行 上述代码时,我得到以下输出 -

Right(Device(Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36,None,None,None,Map()))

如您所见,addlProperties 地图是空的,因为它没有解码器。

代码在githubhere for java and for scala here 我怎样才能在圈子里达到同样的效果。

一个相对简单但难以维护的解决方案是将整个对象解码为 Map 并删除已知键。

您的解码器将变为:

implicit val decodeFoo: Decoder[Device] = Decoder.instance(c =>
  for {
    ua <- c.get[String]("ua")
    dnt <- c.get[Option[Int]]("dnt")
    ip <- c.get[Option[String]]("ip")
    devicetype <- c.get[Option[Int]]("devicetype")
    additional <- c.as[Map[String, Json]].map(_ - "ua" - "dnt" - "ip" - "devicetype")
  } yield new Device(ua, dnt, ip, devicetype, additional)
)

我已将 new Decoder 替换为 Decoder.instance,将 c.downField(...).as[...] 替换为 c.get[...](...)。 这还假设 addlPropertiesMap[String, Json],您可能希望为一组特定类型定义解码器。