1

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>
2
  • 3
    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> Commented Sep 20, 2010 at 19:10
  • It would have been much more helpful if the list was provided in Scala syntax. Commented Sep 21, 2010 at 13:38

2 Answers 2

5

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> 
Sign up to request clarification or add additional context in comments.

Comments

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.

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>

1 Comment

Thank you for your almost epic answer. I really didn't expect anyone to write running code, only a pointer in the right direction had been ok . Big thank you for you valuable effort and I learned a lot form this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.