java - 使用不同类型的数组解析 YAML

标签 java jackson yaml

我正在尝试读取 YAML 文件并将结果存储在 POJO 列表中。

我无法修改 YAML 文件。我使用 Jackson 2.10.0,但我对任何其他版本持开放态度。我正在尝试用 Jackson 解析以下脚本:

车辆-notype.yaml

车辆基本上是一个对象列表,具有一些常见属性和一些车辆类型特有的属性。

---
vehicles:
- car:
  make: "Mercedes-Benz"
  model: "S500"
  topSpeed: 250.0
  seatingCapacity: 5
- truck:
  make: "Isuzu"
  model: "NQR"
  payloadCapacity: 7500.0

期望的输出

读完文件后,我想,如果我反省列表,我想得到:

... App.java:48): -> start()
... App.java:56): class net.jgp.labs.jackson.yaml.lab411_pojos.Car
... App.java:56): class net.jgp.labs.jackson.yaml.lab411_pojos.Truck

CarTruck POJO 非常明显:

汽车

package net.jgp.labs.jackson.yaml.lab411_pojos;

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

public class Car extends Vehicle {

  private int seatingCapacity;
  private double topSpeed;

  @JsonCreator
  public Car(
      @JsonProperty("make") String make,
      @JsonProperty("model") String model,
      @JsonProperty("seating") int seatingCapacity,
      @JsonProperty("topSpeed") double topSpeed) {
    super(make, model);
    this.seatingCapacity = seatingCapacity;
    this.topSpeed = topSpeed;
  }

  public int getSeatingCapacity() {
    return seatingCapacity;
  }

  public void setSeatingCapacity(int seatingCapacity) {
    this.seatingCapacity = seatingCapacity;
  }

  public double getTopSpeed() {
    return topSpeed;
  }

  public void setTopSpeed(double topSpeed) {
    this.topSpeed = topSpeed;
  }

  public String getType() {
    return "car";
  }

}

卡车

package net.jgp.labs.jackson.yaml.lab411_pojos;

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

public class Truck extends Vehicle {

  private double payloadCapacity;

  @JsonCreator
  public Truck(
    @JsonProperty("make") String make, 
    @JsonProperty("model") String model, 
    @JsonProperty("payload") double payloadCapacity) {
      super(make, model);
      this.payloadCapacity = payloadCapacity;
  }

  public double getPayloadCapacity() {
    return payloadCapacity;
  }

  public void setPayloadCapacity(double payloadCapacity) {
    this.payloadCapacity = payloadCapacity;
  }

  @Override
  public String getType() {
    return "truck";
  }

}

舰队

Fleet POJO 也很明显。

package net.jgp.labs.jackson.yaml.lab411_pojos;

import java.util.List;

public class Fleet {

  private List<Vehicle> vehicles;

  public void setVehicles(List<Vehicle> vehicles) {
    this.vehicles= vehicles;
  }

  public List<Vehicle> getVehicles() {
    return vehicles;
  }

}

车辆

Vehicle 有点棘手,因为我正在尝试使用 @JsonTypeInfo@JsonSubTypes。你可以看到注释的代码,它慢慢让我发疯:

package net.jgp.labs.jackson.yaml.lab411_pojos;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.EXTERNAL_PROPERTY
//    ,
//    property = "className"
    )

@JsonSubTypes({
  @Type(value = Car.class, name = "car"),
  @Type(value = Truck.class, name = "truck")
})

//@JsonSubTypes({
//    @Type(value = Car.class, name = "car"),
//    @Type(value = Truck.class, name = "truck")
//})
public abstract class Vehicle {
  private String make;
  private String model;

  @JsonProperty("type")
  abstract public String getType();

  public void setType(String type) {};

  protected Vehicle(String make, String model) {
    this.make = make;
    this.model = model;
  }

  public String getMake() {
    return make;
  }

  public void setMake(String make) {
    this.make = make;
  }

  public String getModel() {
    return model;
  }

  public void setModel(String model) {
    this.model = model;
  }
}

应用程序

最后是应用程序代码,这也非常明显。

package net.jgp.labs.jackson.yaml.lab411_read_diff_objects;

import java.io.File;
import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

import net.jgp.labs.jackson.yaml.lab411_pojos.Fleet;
import net.jgp.labs.jackson.yaml.lab411_pojos.Vehicle;

/**
 * What does it do?
 * 
 * @author jgp
 */
public class ReadListVehicleNoTypeApp {
  private static final Logger log =
      LoggerFactory.getLogger(ReadListVehicleNoTypeApp.class);

  /**
   * main() is your entry point to the application.
   * 
   * @param args
   */
  public static void main(String[] args) {
    ReadListVehicleNoTypeApp app = new ReadListVehicleNoTypeApp();
    try {
      app.start();
    } catch (JsonProcessingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  /**
   * The processing code.
   * 
   * @throws IOException
   */
  protected boolean start() throws IOException {
    log.debug("-> start()");

    ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
    Fleet fleet = mapper.readValue(new File("data/vehicles-notype.yaml"),
        Fleet.class);
    for (Vehicle v : fleet.getVehicles()) {
      log.debug("{}", v.getClass());
    }

    return true;
  }
}

我很确定 @Json 系列属性可以发挥一些作用,但我正在慢慢失去它;-)。

最佳答案

cartruck 是字段名称、属性。我不知道 Jackson 注释允许从不同字段设置类型。

如果Yaml文件无法修改,我们可以使用Streaming API读取类型属性并反序列化Vehicle。在伪代码中它可能看起来像:

while token != EOF
    while token != FIELD_NAME
        nextToken()

    fieldName = nextFieldName();
    clazz = convertToClass(fieldName);
    vehicles.add(read(clazz));

幸运的是,定义类型的字段名称是第一个字段名称,我们可以手动读取它,然后使用 Jackson 读取类型。我从 Vehicle 类中删除了 JsonSubTypesJsonTypeInfo 注释,并使用 Streaming API 它可能如下所示:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLParser;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class YamlApp {

    public static void main(String[] args) throws Exception {
        File yamlFile = new File("./resource/test.yaml").getAbsoluteFile();

        FleetDeserializer deserializer = new FleetDeserializer();
        Fleet fleet = deserializer.readValue(yamlFile);

        System.out.println(fleet);
    }
}

class FleetDeserializer {
    private YAMLFactory factory = new YAMLFactory();
    private ObjectMapper mapper = new ObjectMapper(factory);

    public Fleet readValue(File yamlFile) throws IOException {
        Fleet fleet = new Fleet();
        fleet.setVehicles(new ArrayList<>());

        YAMLParser parser = factory.createParser(yamlFile);
        while (parser.nextToken() != null) {
            if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
                continue;
            }
            // skip everything until a field name
            while (parser.nextToken() != JsonToken.FIELD_NAME) ;

            Class<? extends Vehicle> type = getType(parser.getCurrentName());
            if (type == null) {
                continue;
            }

            // skip field name
            parser.nextToken();
            parser.nextToken();

            // read next vehicle
            fleet.getVehicles().add(mapper.readValue(parser, type));
        }

        return fleet;
    }

    private Class<? extends Vehicle> getType(String fieldName) {
        Objects.requireNonNull(fieldName);
        switch (fieldName) {
            case "car":
                return Car.class;
            case "truck":
                return Truck.class;
            default:
                return null;
        }
    }
}

上面的代码打印:

Fleet{vehicles=[Car{seatingCapacity=5, topSpeed=250.0, make='Mercedes-Benz', model='S500'}, Truck{payloadCapacity=7500.0, make='Isuzu', model='NQR'}]}

关于java - 使用不同类型的数组解析 YAML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58665457/

相关文章:

ruby - 将对象序列化为 JSON、XML、YAML?

java - 如何在运行时在 spring 中重新加载/刷新属性而不重新启动 jvm?

java 小学生招生系统

google-cloud-platform - GCP API 网关 : Cannot use path params

c++ - OpenCV FileStorage 如何读取结构 vector ?

java - Json 使用 Jackson 库序列化 JDK 动态代理

java - 循环逻辑以防止堆栈溢出

java - Spring中如何定义一个String数组的bean(使用XML配置)

java - JSON对象映射器: avoid listing derived class in serialized JSON string

memcached - 如何序列化 Jackson 的 JsonNode 对象?