Scala:从列表列表中创建结构(XML)

时间:2022-04-24 21:22:21

I have a structure from java, a List < List < String > > containing elements like:

我有一个来自java的结构,一个列表< List < String > >,包含如下元素:

[[ "Node0", "Node00", "Leaf0"],
 [ "Node0", "Node00", "Leaf1"],
 [ "Node1", "Leaf2"],
 [ "Node0", "Leaf3"],
 [ "Node2", "Node20", "Node200", "Leaf4"]]

What I want to do is to create a XML structure (using Scala) in the most simple way, ending in something like the below. I could do this is many ways, iterating, recursive calls etc.

我想做的是用最简单的方式创建一个XML结构(使用Scala),以如下所示结尾。我可以用很多方法,迭代,递归调用等等。

Any suggestions for a compact readable way of solving this?

对于解决这个问题有什么建议吗?

<node> Node0 
      <node> Node00 
            <node> Leaf0 </node>
            <node> Leaf1 </node>
      </node>
      <node> Leaf3 </node>
</node>
<node> Node1
      <node> Leaf2 </node>
</node>
<node> Node2
      <node> Node20
            <node> Node200
                  <node> Leaf4 </node>
            </node>
      </node>
</node>

2 个解决方案

#1


3  

Ok, I took a shot at it. I'm also using an attribute, just as others have suggested. There's also a couple of commented lines for a version that will produce elements named after the contents of the list, instead of using attributes.

好吧,我试了一下。我也在使用一个属性,就像其他人建议的那样。还有一些注释行,用于生成以列表内容命名的元素,而不是使用属性。

The hard part of the job is done by the class below, which takes a list of strings and transforms nodes given to it so that they contain the node hierarchy represented by the list.

任务的难点部分由下面的类来完成,它接受一个字符串列表并转换给它的节点,以便它们包含由列表表示的节点层次结构。

import xml._
import transform._

class AddPath(l: List[String]) extends RewriteRule {
  def listToNodes(l: List[String]): Seq[Node] = l match {
    case Nil => Seq.empty
    case first :: rest => 
      <node>{listToNodes(rest)}</node> % Attribute("name", Text(first), Null)
    //case first :: rest => 
      //<node>{listToNodes(rest)}</node> copy (label =  first)
  }

  def transformChild(child: Seq[Node]) = l match {
    case Nil => child
    case first :: rest =>
      child flatMap {
        case elem: Elem if elem.attribute("name") exists (_ contains Text(first)) =>
        //case elem: Elem if elem.label == first =>
          new AddPath(rest) transform elem
        case other => Seq(other)
      }
  }

  def appendToOrTransformChild(child: Seq[Node]) = {
    val newChild = transformChild(child)
    if (newChild == child)
      child ++ listToNodes(l)
    else
      newChild
  }

  override
  def transform(n: Node): Seq[Node] = n match {
    case elem: Elem => elem.copy(child = appendToOrTransformChild(elem.child))
    case other => other
  }
}

After this things become really simple. First, we create the list, and then produce a list of rules from it.

之后事情就变得非常简单了。首先,我们创建列表,然后从中生成规则列表。

val listOfStrings = List(List("Node0", "Node00", "Leaf0"),
                         List("Node0", "Node00", "Leaf1"),
                         List("Node1", "Leaf2"),
                         List("Node0", "Leaf3"),
                         List("Node2", "Node20", "Node200", "Leaf4"))
val listOfAddPaths = listOfStrings map (new AddPath(_))

Next, we create a rule transformer out of these rules.

接下来,我们从这些规则中创建一个规则转换器。

val ruleTransformer = new RuleTransformer(listOfAddPaths: _*)

Finally, we create the XML and pretty print it. Note that I'm adding a root node. If you don't want it, just get it's child. Also note that ruleTransformer will return a Seq[Node] with a single node -- our result.

最后,我们创建XML并漂亮地打印它。注意,我正在添加一个根节点。如果你不想要它,那就买它的孩子。还要注意,ruleTransformer将返回带有单个节点的Seq[Node]——我们的结果。

val results = ruleTransformer(<root/>)
val prettyPrinter = new PrettyPrinter(80, 4)
results foreach { xml =>
  println(prettyPrinter format xml)
}

And the output:

和输出:

<root>
    <node name="Node0">
        <node name="Node00">
            <node name="Leaf0"></node>
            <node name="Leaf1"></node>
        </node>
        <node name="Leaf3"></node>
    </node>
    <node name="Node1">
        <node name="Leaf2"></node>
    </node>
    <node name="Node2">
        <node name="Node20">
            <node name="Node200">
                <node name="Leaf4"></node>
            </node>
        </node>
    </node>
</root>

Output of the alternate version:

备选版本的输出:

<root>
    <Node0>
        <Node00>
            <Leaf0></Leaf0>
            <Leaf1></Leaf1>
        </Node00>
        <Leaf3></Leaf3>
    </Node0>
    <Node1>
        <Leaf2></Leaf2>
    </Node1>
    <Node2>
        <Node20>
            <Node200>
                <Leaf4></Leaf4>
            </Node200>
        </Node20>
    </Node2>
</root>

#2


5  

Try this answer for how to output XML from a collection in Scala.

尝试使用Scala从集合中输出XML。

Also I would suggest that a more readable XML output form would put the node names in an attribute (or a child element) rather than mixing text with other child elements. E.g.

另外,我还建议一个更可读的XML输出表单将节点名放在属性(或子元素)中,而不是将文本与其他子元素混合。如。

<node name="Node0">
  <node name="Node00">

or

<node>
  <name>Node0</name>
  <node>
    <name>Node00</name>
    ...
  </node>
</node> 

#1


3  

Ok, I took a shot at it. I'm also using an attribute, just as others have suggested. There's also a couple of commented lines for a version that will produce elements named after the contents of the list, instead of using attributes.

好吧,我试了一下。我也在使用一个属性,就像其他人建议的那样。还有一些注释行,用于生成以列表内容命名的元素,而不是使用属性。

The hard part of the job is done by the class below, which takes a list of strings and transforms nodes given to it so that they contain the node hierarchy represented by the list.

任务的难点部分由下面的类来完成,它接受一个字符串列表并转换给它的节点,以便它们包含由列表表示的节点层次结构。

import xml._
import transform._

class AddPath(l: List[String]) extends RewriteRule {
  def listToNodes(l: List[String]): Seq[Node] = l match {
    case Nil => Seq.empty
    case first :: rest => 
      <node>{listToNodes(rest)}</node> % Attribute("name", Text(first), Null)
    //case first :: rest => 
      //<node>{listToNodes(rest)}</node> copy (label =  first)
  }

  def transformChild(child: Seq[Node]) = l match {
    case Nil => child
    case first :: rest =>
      child flatMap {
        case elem: Elem if elem.attribute("name") exists (_ contains Text(first)) =>
        //case elem: Elem if elem.label == first =>
          new AddPath(rest) transform elem
        case other => Seq(other)
      }
  }

  def appendToOrTransformChild(child: Seq[Node]) = {
    val newChild = transformChild(child)
    if (newChild == child)
      child ++ listToNodes(l)
    else
      newChild
  }

  override
  def transform(n: Node): Seq[Node] = n match {
    case elem: Elem => elem.copy(child = appendToOrTransformChild(elem.child))
    case other => other
  }
}

After this things become really simple. First, we create the list, and then produce a list of rules from it.

之后事情就变得非常简单了。首先,我们创建列表,然后从中生成规则列表。

val listOfStrings = List(List("Node0", "Node00", "Leaf0"),
                         List("Node0", "Node00", "Leaf1"),
                         List("Node1", "Leaf2"),
                         List("Node0", "Leaf3"),
                         List("Node2", "Node20", "Node200", "Leaf4"))
val listOfAddPaths = listOfStrings map (new AddPath(_))

Next, we create a rule transformer out of these rules.

接下来,我们从这些规则中创建一个规则转换器。

val ruleTransformer = new RuleTransformer(listOfAddPaths: _*)

Finally, we create the XML and pretty print it. Note that I'm adding a root node. If you don't want it, just get it's child. Also note that ruleTransformer will return a Seq[Node] with a single node -- our result.

最后,我们创建XML并漂亮地打印它。注意,我正在添加一个根节点。如果你不想要它,那就买它的孩子。还要注意,ruleTransformer将返回带有单个节点的Seq[Node]——我们的结果。

val results = ruleTransformer(<root/>)
val prettyPrinter = new PrettyPrinter(80, 4)
results foreach { xml =>
  println(prettyPrinter format xml)
}

And the output:

和输出:

<root>
    <node name="Node0">
        <node name="Node00">
            <node name="Leaf0"></node>
            <node name="Leaf1"></node>
        </node>
        <node name="Leaf3"></node>
    </node>
    <node name="Node1">
        <node name="Leaf2"></node>
    </node>
    <node name="Node2">
        <node name="Node20">
            <node name="Node200">
                <node name="Leaf4"></node>
            </node>
        </node>
    </node>
</root>

Output of the alternate version:

备选版本的输出:

<root>
    <Node0>
        <Node00>
            <Leaf0></Leaf0>
            <Leaf1></Leaf1>
        </Node00>
        <Leaf3></Leaf3>
    </Node0>
    <Node1>
        <Leaf2></Leaf2>
    </Node1>
    <Node2>
        <Node20>
            <Node200>
                <Leaf4></Leaf4>
            </Node200>
        </Node20>
    </Node2>
</root>

#2


5  

Try this answer for how to output XML from a collection in Scala.

尝试使用Scala从集合中输出XML。

Also I would suggest that a more readable XML output form would put the node names in an attribute (or a child element) rather than mixing text with other child elements. E.g.

另外,我还建议一个更可读的XML输出表单将节点名放在属性(或子元素)中,而不是将文本与其他子元素混合。如。

<node name="Node0">
  <node name="Node00">

or

<node>
  <name>Node0</name>
  <node>
    <name>Node00</name>
    ...
  </node>
</node>