My scala code currently ends up replacing an entire section of my xml file with the new tag that I'm adding. I want it to only add the tag once as a child of ClientConfig but it replaces all the tags present in this section with itself.
val data = XML.load(file)
val p = new XMLPrettyPrinter(2)
val tryingtoAdd = addNewEntry(data,host,env)
p.write(tryingtoAdd)(System.out)
where host=bob and env=flat are previously defined and addNewEntry is defined as follows
private def isCorrectLocation(parent: Elem, node: Elem, host: String): Boolean = {
parent.label == "ClientConfig" && node.label == "host"
}
def addNewEntry(elem:Elem, host: String, env: String): Elem ={
val toAdd = <host name={host} env={env} />
def addNew(current: Elem): Elem = current.copy(
child = current.child.map {
case e: Elem if isCorrectLocation(current, e, host) ⇒ toAdd
case e: Elem ⇒ addNew(e)
case other ⇒ other
}
)
addNew(elem)
}
The xml it produces is
<ClientConfig>
<host name="bob" env="flat"/>
<host name="bob" env="flat"/>
<host name="bob" env="flat"/>
<host name="bob" env="flat"/>
</ClientConfig>
where instead I want it to just append it as a single child of ClientConfig such as this where the last three children were already present in the file
<ClientConfig>
<host name="bob" env="flat"/>
<host name="george" env="flat"/>
<host name="alice" env="flat"/>
<host name="bernice" env="flat"/>
</ClientConfig>
What do i do? For example python has a simple insert method
In your case, when the pattern match goes to
case e: Elem if isCorrectLocation(current, e, host) => toAdd
The toAdd method will use the host, env you pass in addNewEntry(data, host, env)
. bob for host, flat for env. So, toAdd will always return <host name="bob" env="flat"/>
.
Assume you have the client.xml like this:
<Root>
<ServerConfig>
<host name="allen" env="flat"/>
</ServerConfig>
<ClientConfig>
<host name="george" env="flat"/>
<host name="alice" env="flat"/>
<host name="bernice" env="flat"/>
</ClientConfig>
</Root>
The following code is how I try to get it done.
def toBeAddedEntry(name: String, env: String) = <host name={ name } env={ env } />
def addNewEntry(originalXML: Elem, name: String, env: String) = {
originalXML match {
case e @ Elem(_, _, _, _, configs @ _*) => {
val changedNodes = configs.map {
case <ClientConfig>{ innerConfigs @ _* }</ClientConfig> => {
<ClientConfig> { toBeAddedEntry(name, env) ++ innerConfigs }</ClientConfig>
}
case other => other
}
e.copy(child = changedNodes)
}
case _ => originalXML
}
}
val originalXML = XML.load("client.xml")
val printer = new scala.xml.PrettyPrinter(80,5)
println(printer.format(addNewEntry(originalXML, "bob", "flat")))
// result
<Root>
<ServerConfig>
<host env="flat" name="allen"/>
</ServerConfig>
<ClientConfig>
<host name="bob" env="flat"/>
<host env="flat" name="george"/>
<host env="flat" name="alice"/>
<host env="flat" name="bernice"/>
</ClientConfig>
</Root>
Besides, I notice one thing during the process. XML.load actually reverses attribute order, maybe it's irrelevant for solving your problem but just adding it here in case you need it.