how to edit xml file and keep comments

475 views
Skip to first unread message

danisevsky

unread,
Nov 15, 2012, 3:47:00 PM11/15/12
to scala...@googlegroups.com
Hi,

I have couple of xml files which I would like to edit with scala. For example this file (web.xml):

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app>
<!--     <context-param> -->
<!--         <param-name>log4jConfigLocation</param-name> -->
<!--         <param-value>classpath:log4j-deployment.properties</param-value> -->
<!--     </context-param> -->
  <filter>
        <filter-name>wicket</filter-name>
        <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
        <init-param>
            <param-name>applicationFactoryClassName</param-name>
            <param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value>
        </init-param>
        <init-param>
            <param-name>applicationBean</param-name>
            <param-value>wicketApplication</param-value>
        </init-param>
        <init-param>
            <param-name>configuration</param-name>
            <param-value>deployment</param-value>
        </init-param>
  </filter>
</web-app>

I edit with this simple application:

import scala.xml._
import scala.xml.transform._

object App extends App {

  val xml1 = XML.loadFile("web.xml")
  val xml2 = rt2(xml1)
  XML.save("web_2.xml", xml2)

}

object t1 extends RewriteRule {
  override def transform(n: Node): Seq[Node] = n match {
    case Elem(prefix, "param-value", attribs, scope, Text("deployment")) =>
      Elem(prefix, "param-value", attribs, scope, Text("development"))
    case other => other
  }
}

object rt1 extends RuleTransformer(t1)

object t2 extends RewriteRule {
  override def transform(n: Node): Seq[Node] = n match {
    case sn @ Elem(_, "init-param", _, _, _*) => rt1(sn)
    case other => other
  }
}

object rt2 extends RuleTransformer(t2)

And I get this result (web_2.xml):

<web-app>




  <filter>
        <filter-name>wicket</filter-name>
        <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
        <init-param>
            <param-name>applicationFactoryClassName</param-name>
            <param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value>
        </init-param>
        <init-param>
            <param-name>applicationBean</param-name>
            <param-value>wicketApplication</param-value>
        </init-param>
        <init-param>
            <param-name>configuration</param-name>
            <param-value>development</param-value>
        </init-param>
  </filter>
</web-app>

Which is fine except one small detail. First line (<?xml version="1.0" encoding="ISO-8859-1"?>) and comments are missing. Could you please tell me how to fix it?

Thanks in advance!

"Ionuț G. Stan"

unread,
Nov 16, 2012, 5:05:13 AM11/16/12
to scala...@googlegroups.com
I have taken a look in the source code of Scala's XML utilities and the
bad news is that the SAX parser that Scala uses to parse the XML
document when you call XML.loadFile does not parse comments and the XML
declaration.

To be honest, for the problem you've exemplified here I'd probably use
some regular expressions. You don't seem to need to advanced parsing
capabilities, some match and replace should be just fine.

Take a look at this:

https://github.com/scala/scala/blob/v2.9.2/src/library/scala/xml/factory/XMLLoader.scala#L28

--
Ionuț G. Stan | http://igstan.ro

danisevsky

unread,
Nov 22, 2012, 2:58:25 PM11/22/12
to scala...@googlegroups.com
Hi,

thanks for your advice. I was quite disappointed that I can't use scala xml support. However I have another xml which I would like to process with scala. It is classical maven pom.xml file:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>myproject</artifactId>
    <packaging>war</packaging>
    <version>6.2.0</version>
    <name>myproject</name>

<!--     </repository> -->
<!--         <repository> -->
<!--             <id>jboss-repo</id> -->
<!--             <url>http://repository.jboss.org/nexus/content/groups/public</url> -->
<!--         </repository> -->
<!--     </repositories> -->

    <dependencies>

        <!-- WICKET DEPENDENCIES -->
        <dependency>
            <groupId>org.apache.wicket</groupId>
            <artifactId>wicket-core</artifactId>
            <version>6.2.0</version>
        </dependency>

    </dependencies>

<!--     ... -->

</project>


And I need to increase version of 'myproject'. So instead of 6.2.0 will be 6.2.1 and version of wicket remains 6.2.0. I wrote simple app which works good but code doesn't look nice and clean:


import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import scala.io.Source

object App4 extends App {

  val Pom = """(.*)<packaging>war</packaging>(.*)<version>(\d+)\.(\d+)\.(\d+)</version>(.*)<name>myproject</name>(.*)""".r
  val partStart = "<packaging>war</packaging>\n\t<version>";
  val partEnd = "</version>\n\t<name>myproject</name>";

  processPom(new File("pom.xml"))

  def processPom(file: File) {
    val fileContents = Source.fromFile(file.getAbsolutePath()).mkString
    if (fileContents != null && !fileContents.isEmpty) {
      val Pom(_, _, f, s, t, _, _) = fileContents.filter(_ != '\n')
      val oldVersion = f + "." + s + "." + t
      val newVersion = f + "." + s + "." + (t.toInt + 1)
      chnageVersion(file, fileContents, oldVersion, newVersion);
    }

    def chnageVersion(file: File, fileContents: String, oldVersion: String, newVersion: String) {
      val regex = (partStart + oldVersion + partEnd).r
      val replc = (partStart + newVersion + partEnd);
      val newFileContents = regex.replaceAllIn(fileContents, replc)
      changeFileContent(file, newFileContents);

      def changeFileContent(file: File, fileContents: String) {
        val newFile = new File(file.getName + ".conv")
        val writer = new BufferedWriter(new FileWriter(newFile))
        writer.write(fileContents, 0, fileContents.length)
        writer.close
        file.delete
        newFile.renameTo(file)
      }
    }
  }
}


Could you please hint me how to solve it more elegant and scala way?

Thanks in advance!


Dne čtvrtek, 15. listopadu 2012 21:47:00 UTC+1 danisevsky napsal(a):

Daniel Sobral

unread,
Nov 22, 2012, 5:10:42 PM11/22/12
to danisevsky, scala...@googlegroups.com

You should look into scales, an alternative xml library for scala.

Brian Smith

unread,
Nov 22, 2012, 6:06:04 PM11/22/12
to danisevsky, scala...@googlegroups.com
Hi

This will work:

import scala.xml.parsing.ConstructingParser  

object App extends App {

  val xml1 = ConstructingParser.fromFile(new File("web.xml"), preserveWS = true).document.docElem
  val xml2 = rt2(xml1)
  XML.save("web_2.xml", xml2, enc = "ISO-8859-1", xmlDecl = true)

}

ConstructingParser uses a Handler that processes comments, and you can force the production of the XML declaration by supplying the additional arguments to save().

Whilst I'd definitely look at scales or anti-xml for anything more involved, for simple manipulations this is probably easier.

Regards

Brian
Reply all
Reply to author
Forward
0 new messages