3

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

Zee
  • 1,321
  • 2
  • 18
  • 41

2 Answers2

2

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.

Allen Chou
  • 1,229
  • 1
  • 9
  • 12
  • Where is the method toBeAddedEntry coming from? It seems to throw an error when I compile this – Zee Aug 03 '15 at 13:30
  • @Zee, sorry my fault, just added it. – Allen Chou Aug 03 '15 at 13:33
  • Fixed the last issue! One more question, what is configs supposed to be here? – Zee Aug 03 '15 at 13:59
  • 1
    @Zee, If you look at the definition of Elem, you will know that configs with type Seq[Node] actually mean the Node between , the simple way to test it is to print it out. – Allen Chou Aug 03 '15 at 14:07
  • it seems to add the node outside of the clientconfig. When i specify a location to add such as (originalXML \\ "ClientConfig") it gives a type mismatch. How are you getting it to add inside of the ClientConfig tag – Zee Aug 03 '15 at 14:20
  • 1
    @Zee, hey , why are you doing this originalXML \\ "ClientConfig", I edited my answer. It seems that you are not clear about the structure of originalXML. I use pattern to extract the Seq[Node] of ClientConfig and then make some modifications. – Allen Chou Aug 03 '15 at 14:33
  • The issue is that it seems to arbitrarily add it above and outside the ClientConfig area of the xml file. Are you just using the entire file in your XML load method? – Zee Aug 03 '15 at 14:41
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/85016/discussion-between-allen-and-zee). – Allen Chou Aug 03 '15 at 14:42
  • 1
    Sorry for that, yes, I just load the entire file in XML load. So, what am i missing , please tell me. – Allen Chou Aug 03 '15 at 14:47
  • There is xml before the ClientConfig tag and when I use your code, it ends up adding it outside of the ClientConfig tag so for example it adds it – Zee Aug 03 '15 at 14:50
  • 1
    So you mean in the file I am gonna load, the structure is actually like , and you wanna add as the first child of , am I right? – Allen Chou Aug 03 '15 at 14:54
  • 1
    @Zee, hey, check the edited answer, hope this time I get it right. – Allen Chou Aug 03 '15 at 15:24
  • @Zee, ha ha, thank god, I am also kind of worn out. Hey, I happen to notice that you have asked a few questions about Scala XML, so I suggest you have a deep read of Working with XML chapter in Programming In Scala, it may help a lot. Besides, you'd better offer more details about the question you are gonna ask next time to help others understand your problems better. In you case, the structure of the xml file. you see we both waste a lot time on clarifying the real problem of question. Anyway, get it done. – Allen Chou Aug 03 '15 at 15:34
  • I actually read the whole chapter on Scala XML from Odersky's book but I'm very new to scala and the examples in the book were very basic compared to this. Regardless, im still continuing to read it – Zee Aug 03 '15 at 15:39
  • Truly is, when I was a newbie, I started to read that book, also confused. Yes, you should continue. After solving a series of Scala XML problems, I bet you will better understanding of that chapter. So, that's it, good luck!! – Allen Chou Aug 03 '15 at 15:45
0

https://github.com/geirolz/advxml

it's a simple library in order to simplify the XML transformation

David Geirola
  • 616
  • 3
  • 17