Scala: Create structure (XML) from Lists of Lists
I have a structure from java, a List < List < String > > containing elements like:
[[ "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.
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>
Try this answer for how to output XML from a collection in Scala.
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.
<node name="Node0">
<node name="Node00">
or
<node>
<name>Node0</name>
<node>
<name>Node00</name>
...
</node>
</node>
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.
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>
精彩评论