Scala fastxml杰克逊。读取Yaml文件,但返回数据结构难以使用

时间:2018-10-16 22:26:19

标签: scala parsing jackson yaml fasterxml

我正在编写一个scala方法,该方法读取Yaml文件并返回Yaml文件内容的Map。我可以成功完成此操作,但是使用数据结构非常麻烦,这将在下面进行演示。

请注意,我可以并且已经在scala中使用jackson来获取yaml文件并将其构成为case类。效果很好,使用起来也不麻烦。在这个问题中,yaml是动态的,因此我们需要将其放入动态数据结构中,即Map或Map List

在Java中,解决问题没有问题。返回的数据结构易于使用。

Java示例:

    public Map readMapYml(String fullFileName) {
    ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
    try {
        return mapper.readValue(new File(fullFileName), Map.class);
    } catch (Exception e) {
        throw new RuntimeException("JavaParser->writeYml: 
           Unable to write yaml file: " + e.getMessage());
    }
}

我等效的scala代码。 (我还尝试了下面的代码的许多变体)

def readMapYml(fullFileName: String): Map[String,Any] = {
  val mapper = new ObjectMapper(new YAMLFactory())
  mapper.registerModule(DefaultScalaModule)
  try {
    mapper.readValue(new File(fullFileName), classOf[Map[String,Any]])
  }
  catch {
    case e: Exception =>
      throw new RuntimeException("Parser->readMapYml: Unable to read yaml 
    file to map.  filename: " + fullFileName + " Message: " + e.getMessage)
   }
 }

所以这可行,我可以解析数据,但这确实很麻烦。

麻烦的例子:

   result.get("groups").get.asInstanceOf[List[Map[String,Any]]](0).get("group").get.asInstanceOf[Map[String,Any]].get("colors").get.asInstanceOf[List[Map[String,Any]]](0).get("color").get

Btw互操作性很好,我可以用Java编写并从scala调用它。但是,在这种情况下,我们需要使我们的Scala代码正常工作

我的问题:我希望fastxml Jackson返回一个数据结构,该结构易于使用,类似于我通过Java获得的数据结构。我该怎么办?

2 个答案:

答案 0 :(得分:0)

以下代码可以很好地导航从Jackson返回的kv地图。

  /**
* Purpose is to parse through a generic kv map of data returned from Jackson.
* @param structure data return from Jackson or a sub-structure of data returned from 
  Jackson
* @param path  A path to the data we want to return.  A stack so order is leaf to 
  branch to branch .... to root
* @return the section requested.  The first item added to your stack.  In other words 
  the last item pop.
*/

def getStructure(结构:任何,路径:mutable.Stack [String]):任何= {

var retVal: Any = structure

if (path.nonEmpty) {
  structure match {
    case map: Map[String, Any] =>
      retVal = map.get(path.pop())
    case some: Some[Any] =>
      retVal = some.get
    case list: List[Any] =>
      retVal = list(path.pop().toInt)
    case None =>
      throw new IllegalStateException("DataHelper->getStructure: Bad path keyword does not exist in section of path.  remaining stack: " + path)
    case _ =>
      throw new IllegalStateException("DataHelper->getStructure: Structure type is unexpected.  Type: " + structure.getClass.getName)
  }
  if (path.nonEmpty) {
    retVal = getStructure(retVal, path)
  }
}

retVal match {
  case some: Some[Any] =>
    retVal = some.get //If the last item is a some get the content of the some.
  case _ =>
}

retVal

}

测试代码:

test("testMyDataHelper") {
val mapParser = new MapParser
val result = mapParser.readMapYml("test.yaml")
var path = mutable.Stack[String]()

path.push("name","0","groups")
println(DataHelper.getStructure(result, path))//Joe
path.push("name","1","groups")
println(DataHelper.getStructure(result, path))//Bill
path.push("part2","0","items","0","groups")
println(DataHelper.getStructure(result,path))//dash
path.push("part2","2","items","0","groups")
println(DataHelper.getStructure(result,path))//line
//Example of getting a subsection of yaml
path.push("items","0","groups")
val subsection = DataHelper.getStructure(result,path)
//use the subsection
path.push("part1","2")
println(DataHelper.getStructure(subsection,path))//green
path.push("part2","0")
println(DataHelper.getStructure(subsection,path))//dash

}

yaml文件

document: "0.0.1"
groups:
- version: "0.0.0"
  type: "A"
  name: "Joe"
  agency: "skjenco"
  number: 8
  items:
  - part1: "red"
    part2: "dash"
  - part1: "brown"
    part2: "underline"
  - part1: "green"
    part2: "line"
- version: "0.0.1"
  type: "B"
  name: "Bill"
  agency: "billco"
  number: 3
  items:
  - part1: "orange"
    part2: "line"
  - part1: "pink"
    part2: "dash"
  - part1: "blue"
    part2: "line"

答案 1 :(得分:0)

以下方法的困难之一是,它要求每次提取数据时都要定义数据类型,而数据占用的数据类型为Any-因此迫使我们为值定义数据类型。

mapper.readValue(new File(fullFileName), classOf[Map[String,Any]])

由于预期YAML文件是动态的,因此最好使用来自Jackson的更加发达的JsonNode数据类型,并利用以下方法:

  val yaml = mapper.readValue(new File(fullFileName), classOf[Any])
  val jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(yaml)
  val jsonObj = mapper.readTree(jsonString)

生成的jsonObj的数据类型为JsonNode,它将具有动态模式并通过其内置方法满足数据转换/类型转换需求