4

XML ファイル内のテキストを置換したいのですが、ソース ファイル内のその他の書式は保持します。

たとえば、DOM として解析し、XPath を使用してノードを置き換え、文字列として出力すると、ファイル全体が再フォーマットされるため、うまくいかない場合があります。(きれいな印刷は 99% の場合に適しているかもしれませんが、「きれい」でなくても、既存の書式設定を保持する必要があります)

文字列をDOMツリーとして解析せずに「検索と置換」を実行できるJava/Scalaライブラリはありますか? または、少なくとも元のフォーマットを保持できますか?

編集:

maven replacer プラグインこのようなことをしていると思います。使用して元の空白の書式設定を保持しているようですsetPreserveSpace(試してみる必要があると思います)。

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer; 
...
   private String writeXml(Document doc) throws Exception {
            OutputFormat of = new OutputFormat(doc);
            of.setPreserveSpace(true);
            of.setEncoding(doc.getXmlEncoding());

            StringWriter sw = new StringWriter();
            XMLSerializer serializer = new XMLSerializer(sw, of);
            serializer.serialize(doc);
            return sw.toString();
    }

したがって、質問は次のように変わります:余分な依存関係なしでそうする(簡単な)方法はありますか?

EDIT2:

要件は、外部で提供される XPath クエリを使用することです。つまり、文字列として使用します。

4

2 に答える 2

2

scala.xml と、それがどれだけ嫌いかを簡単に思い出せるようにコードを作成するつもりでした。最初にScalaを学んで以来、私はそれを使用していません。

通常、空白のテキスト ノードが表示されます。これは、PiSの「カタログ」の例で言及されています

読み込み時に属性を逆にすることは覚えていまし

ただし、コンパイラは xml リテラルの属性を逆にしません。したがって、xpath を動的に提供する必要がある場合は、コンパイラ ツールボックスを使用してソース ドキュメントをリテラルとしてコンパイルし、xpath 文字列を / 演算子に変換してコンパイルすることもできます\

これはすぐに使えるちょっとした楽しみですが、おそらく標準の Scala ディストリビューションのみを使用する必要がある場合は、適用性のスイート スポットがあるかもしれません。

後で試してみる機会があれば更新します。

import scala.xml._
import java.io.File

object Test extends App {
  val src =
"""|<doc>
   |  <foo bar="red" baz="yellow"> <bar> red </bar> </foo>
   |  <baz><bar>red</bar></baz>
   |</doc>""".stripMargin

  val red = "(.*)red(.*)".r
  val sub = "blue"

val tmp =
<doc>
   <foo bar="red" baz="yellow"> <bar> red </bar> </foo>
   <baz><bar>red</bar></baz>
</doc>

  Console println tmp

  // replace "red" with "blue" in all bar text

  val root = XML loadString src
  Console println root
  val bars = root \\ "bar"
  val barbars =
    bars map (_ match {
      case <bar>{Text(red(prefix, suffix))}</bar> =>
           <bar>{Text(s"$prefix$sub$suffix")}</bar>
      case b => b
    })
  val m = (bars zip barbars).toMap
  val sb = serialize(root, m)
  Console println sb

  def serialize(x: Node, m: Map[Node, Node], sb: StringBuilder = new StringBuilder) = {
    def serialize0(x: Node): Unit = x match {
      case e0: Elem =>
        val e = if (m contains e0) m(e0) else e0
        sb append "<"
        e nameToString sb
        if (e.attributes ne null) e.attributes buildString sb
        if (e.child.isEmpty) sb append "/>"
        else {
          sb append ">"
          for (c <- e.child) serialize0(c)
          sb append "</"
          e nameToString sb
          sb append ">"
        }
      case Text(t) => sb append t
    }
    serialize0(x)
    sb
  }
}
于 2013-08-14T17:53:57.290 に答える
1

scala.xml.pullXMLを試したり、スケーリングしたりできます。

ファイルを解析するための実際のコードは、ここにあります

Scales XML は、ストリーミング API である STAX API を使用できます。そのため、完全な DOM は存在せず、通常、あまり前処理を行わずに XML の一部に到達します。

特別な形式の XML ファイルでテストし、うまくいくかどうかを確認してください。

単純なテキスト検索を使用して XML に置き換えることはお勧めしません。ミスマッチの可能性は十分にあります。次に、予測できない方法でドキュメントを変更します。結果として生じるバグは、通常、見つけるのが困難です。

Scales XML で短い実験を行ったところ、非常に有望に見えます。

    scala> import scales.utils._
    import scales.utils._
    scala> import ScalesUtils._
    import ScalesUtils._
    scala> import scales.xml._
    import scales.xml._
    scala> import ScalesXml._
    import ScalesXml._
    scala> import scales.xml.serializers.StreamSerializer
    import scales.xml.serializers.StreamSerializer
    scala> import java.io.StringReader
    import java.io.StringReader
    scala> import java.io.PrintWriter
    import java.io.PrintWriter

    scala> def xmlsrc=new StringReader("""
         | <a attr1="value1"> <b/>This
         | is some tex<xt/>
         |   <!-- A comment -->
         |   <c><d>
         |   </d>
         |   <removeme/>
         |   <changeme/>
         | </c>
         | </a>
         | """)
    xmlsrc: java.io.StringReader

    scala> def pull=pullXml(xmlsrc)
    pull: scales.xml.XmlPull with java.io.Closeable with scales.utils.IsClosed

    scala> writeTo(pull, new PrintWriter(System.out))
    <?xml version="1.0" encoding="UTF-8"?><a attr1="value1"> <b/>This
    is some tex<xt/>
      <!-- A comment -->
      <c><d>
      </d>
      <removeme/>
      <changeme/>
    </c>
    res0: Option[Throwable] = None

    scala> def filtered=pull flatMap {
         |   case Left(e : Elem) if e.name.local == "removeme" => Nil
         |   case Right(e : EndElem) if e.name.local == "removeme" => Nil
         |   case Left(e : Elem) if e.name.local == "changeme" => List(Left(Elem("x")), Left(Elem("y"
     Right(EndElem("x")))
         |   case Right(e : EndElem) if e.name.local == "changeme" => List(Right(EndElem("x")))
         |   case otherwise => List(otherwise)
         | }
    filtered: Iterator[scales.xml.PullType]

    scala> writeTo(filtered, new PrintWriter(System.out))
    <?xml version="1.0" encoding="UTF-8"?><a attr1="value1"> <b/>This
    is some tex<xt/>
      <!-- A comment -->
      <c><d>
      </d>

      <x><y/></x>
    </c>
    res1: Option[Throwable] = None

この例では、最初に XML トークン ストリームを初期化します。次に、トークン ストリームを変更せずに出力します。コメントとフォーマットが保持されていることがわかります。次に、トークン ストリームをモナド Scala API で変更し、結果を出力します。ほとんどの書式設定が保持され、変更された部分の書式設定のみが異なることがわかります。

したがって、Scales XML は問題を簡単に解決するように見えます。

于 2013-08-14T08:59:19.350 に答える