在 YAML 嵌套数据中搜索值

我目前正在尝试将 YAML 文件解析为输入/配置以运行某些测试。问题是:使用杰克逊,无论我为它设计的结构如何,嵌套数据似乎都不适合这个类,几乎每次我得到这样的东西:


com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token

我打算使用与 XPath 类似的方法简单地“搜索” YAML 文件中的数据,而不用担心映射对象和有限级别的嵌套。


这是示例类:


public class YAMLInput {


    private ArrayList<SomeContainer> containers;

    //getter and setters


    private class SomeContainer {

        private String name; 

        private String path;

        private ArrayList<Integer> intList;

        private ArrayList<String> strList;

        private ArrayList<SomeObject> someObjList;


        private class SomeObject {

             private String objectName;

             private ArrayList<String> strList;

        }


    }

}

Yaml 输入:


container:

    name: Cont1

    path: /storage/outputFolder

    intList: 

        - 100

        - 200

        - 300

    strList:

        - strFirst

        - strSecond

        - strThird

    someObjList: 

        obj1: 

          objName: strname

          strList: 

             - 100

             - 200

             - 300

        obj2:

          # (...)


这个想法是为YAMLInput类构建一个构造函数:


public YAMLInput( SearchableYAMLData data) {

   for(SearchableYAMLData container : data.getList("container")){

      this.containers.add( new SomeContainer());

      this.containers.get(0) = container.get("name");

      //...

   }

}

最接近这个假设SearchableYAMLData类的可用工具是什么?


暮色呼如
浏览 168回答 2
2回答

狐的传说

您得到的错误可能源于您显示的 YAML 与您显示的类不对应。someObjList在您的 YAML 数据中是一个映射(包含键值对,第一个键是obj1),而在您的类中,它是一个ArrayList<SomeObject>. 这对应于您的 YAML 数据中的一个序列,应该如下所示:someObjList:&nbsp;&nbsp; &nbsp; - objName: strname&nbsp; &nbsp; &nbsp; strList:&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- 100&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- 200&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- 300&nbsp; &nbsp; - # (...)但是,我不确定,因为您并没有真正显示产生错误的代码。话虽如此,如果您正在寻找一种通过任意 YAML 进行搜索的方法,请不要使用 Jackson。Jackson 是一个(反)序列化的工具,你不想反序列化你的 YAML;你只想走它的结构。为此,您可以使用 SnakeYAML,它是 Jackson 使用的 YAML 解析器:Yaml yaml = new Yaml();Node root = yaml.compose(new StringReader("foo: bar"));root将是 a ScalarNode、 aMappingNode或 a SequenceNode。后两个将包含您可以下降的子节点。这种结构对于类似 XPath 的搜索当然是可行的。如果您追求性能,更快的方法是使用parseSnakeYaml 的顺序接口。基本上,您不断地从解析器中查询下一个事件并检查您正在搜索的路径是否包含它。如果是这样,继续查询它的子元素并在那里搜索路径中的下一个元素。如果没有,解析并转储当前事件的所有子内容,然后继续搜索当前路径元素。如果您可以阅读 Python,您可以从我的回答中获得一些灵感,该回答通过事件解析输入 YAML,并且您可以指定要附加数据的路径。

人到中年有点甜

您会看到Cannot deserialize instance of "java.util.ArrayList" out of START_OBJECT token,因为在您定义的根级别ArrayList<SomeContainer> containers但YAML文件包含object. 为避免这种情况,我们需要配置ObjectMapper为接受单个对象,例如array:ObjectMapper mapper = new ObjectMapper(new YAMLFactory());mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);此外,obj1并且obj2未在您的模型中定义。所以你应该删除它们或创建额外的包装对象。但如果您只需要遍历YAML文件,您可以将其读取为Map. 下面的代码:import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;import java.io.File;import java.util.Map;public class YamlApp {&nbsp; &nbsp; public static void main(String[] args) throws Exception {&nbsp; &nbsp; &nbsp; &nbsp; File yamlFile = new File("./resource/test.yaml").getAbsoluteFile();&nbsp; &nbsp; &nbsp; &nbsp; ObjectMapper mapper = new ObjectMapper(new YAMLFactory());&nbsp; &nbsp; &nbsp; &nbsp; Map yaml = mapper.readValue(yamlFile, Map.class);&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(yaml);&nbsp; &nbsp; }}印刷:{container={name=Cont1, path=/storage/outputFolder, intList=[100, 200, 300], strList=[strFirst, strSecond, strThird], someObjList={obj1={objName=strname, strList=[100, 200, 300]}, obj2={objName=strname2, strList=[1002, 2002, 3002]}}}}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java