Reputation: 2803
Beginner to Scala here coming from a Java background. Suppose I have a bunch of XML just floating around in my code:
val x = <file> <name> some-file.txt </name> </file>
Is there any way I can extract the XML elements into named constants? I've tried the following, but it doesn't work as on the first line it's still expecting a closing </file>
tag:
val FileStart = <file>
val FileEnd = </file>
I ask because I want to avoid magic values floating around in my code. I cringe at the thought of using the <name>
tag 100 times and then having its value change ten months down the road (which would result in a terrible tag hunt). I'd much rather have it defined in a constant somewhere.
Better yet, is there a Scala way to efficiently approach this problem? Maybe I'm stuck in Java thinking.
Upvotes: 1
Views: 226
Reputation: 39587
Extraction:
scala> val x = <file> <name> some-file.txt </name> </file>
x: scala.xml.Elem = <file> <name> some-file.txt </name> </file>
scala> import xml.Elem
import xml.Elem
scala> x match { case Elem(prefix, label, attrs, ns, children @ _*) => println(label) }
file
scala> val File = "file"
File: String = file
scala> x match { case Elem(prefix, File, attrs, ns, children @ _*) => println(children) }
ArrayBuffer( , <name> some-file.txt </name>, )
scala> x match { case Elem(prefix, File, attrs, ns, children @ _*) => children \\ "name" }
res4: scala.xml.NodeSeq = NodeSeq(<name> some-file.txt </name>)
File
must not start with a lower-case letter, otherwise it is a variable.
Well, that's if you're extracting nodes without using literal tags in the pattern.
If you want to construct nodes without using literals, you can do that, too:
scala> val n = <name> some-file.txt </name>
n: scala.xml.Elem = <name> some-file.txt </name>
scala> val file = "file"
file: String = file
scala> import xml._
import xml._
scala> val f = Elem(null, file, Null, TopScope, minimizeEmpty = false, n)
f: scala.xml.Elem = <file><name> some-file.txt </name></file>
Here is a nice syntax:
https://github.com/lihaoyi/scalatags#hello-world
That focuses on producing html text.
scala> val tag = "file"
tag: String = file
scala> val file: Node => Node = Elem(null, tag, Null, TopScope, true, _)
file: scala.xml.Node => scala.xml.Node = <function1>
scala> file { <name> some-file.txt </name> }
res0: scala.xml.Node = <file><name> some-file.txt </name></file>
Upvotes: 1
Reputation: 2618
Your suggestion is not a good one, as the following:
val FileStart = <file>
val FileEnd = </file>
doesn't create and assign to a variable a valid XML nodes, which is required and validated during compilation. Even more unexpectedly Scala compiler will interpret the above as definition of a single node FileStart, as a <file>val FileEnd = </file>
XML node ;)
Since you're worried of having multiple name
tags for which you might need to change the node name, I'd rather suggest defining a set of separate reusable methods i.e.:
def nameNode(name : String) = <name>{name}</name>
def fileNode(name : String) = <file>{nameNode(name)}</file>
def createXml(files : List[String]) = <myLovelyFiles>
{files.map(fileNode)}
</myLovelyFiles>
This way you will not only avoid duplicating tag name 100 times, but also should you need to test some complex functionality when constructing XML you've got a small testable units for it.
Hope this helps.
Upvotes: 4