0

したがって、私の問題はこの以前の StackOverflow questionとほぼ同じですが、受け入れられた回答が気に入らないため、質問を再質問しています。

連結された XML ドキュメントのファイルがあります。

<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
...
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>

それぞれ分解してみたいと思います。

私が知る限り、scala.xml.XMLファイル/文字列モデルごとに 1 つのドキュメントに依存するため、使用できません。

Parser入力ソースから XML ドキュメントを解析するために使用できるサブクラスはありますか? それなら、私は何かのようなことをすることができたからですmany1 xmldoc

4

2 に答える 2

0

安全性が気になる場合は、チャンクを一意のタグでラップできます。

def mkTag = "block"+util.Random.alphanumeric.take(20).mkString
val reader = io.Source.fromFile("my.xml")
def mkChunk(it: Iterator[String], chunks: Vector[String] = Vector.empty): Vector[String] = {
  val (chunk,extra) = it.span(s => !(s.startsWith("<?xml") && s.endsWith("?>"))
  val tag = mkTag
  def tagMe = "<"+tag+">"+chunk.mkString+"</"+tag+">"
  if (!extra.hasNext) chunks :+ tagMe
  else if (!chunk.hasNext) mkChunk(extra, chunks)
  else mkChunk(extra, chunks :+ tagMe)
}
val chunks = mkChunk(reader.getLines())
reader.close
val answers = xml.XML.fromString("<everything>"+chunks.mkString+"</everything>")
// Now take apart the resulting parse

一意の囲みタグを指定したため、誰かがリテラル XML タグを途中のどこかに埋め込んだ場合に解析エラーが発生する可能性がありますが、間違った数の解析を誤って取得することはありません。

(警告: コードは入力されていますが、まったくチェックされていません。正確な動作ではなく、アイデアを提供するためのものです。)

于 2012-04-17T21:32:35.937 に答える
0

わかりました、私はもっと満足している答えを思いつきました。

基本的に、私は a を使用して XML を解析しようとしSAXParserますが、パーサーが間違った場所で a に遭遇したことを示す s をscala.xml.XML.load監視します。SAXParseException<?xml

次に、すでに解析されているルート要素を取得し、入力を巻き戻して、そこから解析を再開します。

// An input stream that can recover from a SAXParseException 
object ConcatenatedXML {
  // A reader that can be rolled back to the location of an exception
  class Relocator(val re : java.io.Reader)  extends java.io.Reader {
    var marked = 0
    var firstLine : Int = 1
    var lineStarts : IndexedSeq[Int] = Vector(0)
    override def read(arr : Array[Char], off : Int, len : Int) = { 
      // forget everything but the start of the last line in the
      // previously marked area
      val pos = lineStarts(lineStarts.length - 1) - marked
      firstLine += lineStarts.length - 1

      // read the next chunk of data into the given array
      re.mark(len)
      marked = re.read(arr,off,len)

      // find the line starts for the lines in the array
      lineStarts = pos +: (for (i <- 0 until marked if arr(i+off) == '\n') yield (i+1))

      marked
    }
    override def close { re.close }
    override def markSupported = false
    def relocate(line : Int, col : Int , off : Int) {
      re.reset
      val skip = lineStarts( line - firstLine ) + col + off
      re.skip(skip)
      marked = 0
      firstLine = 1
      lineStarts = Vector(0)
    }
  }

  def parse( str : String ) : List[scala.xml.Node] = parse(new java.io.StringReader(str))
  def parse( re : java.io.Reader ) : List[scala.xml.Node] = parse(new Relocator(re))

  // parse all the concatenated XML docs out of a file
  def parse( src : Relocator ) : List[scala.xml.Node] = {
    val parser = javax.xml.parsers.SAXParserFactory.newInstance.newSAXParser
    val adapter = new scala.xml.parsing.NoBindingFactoryAdapter

    adapter.scopeStack.push(scala.xml.TopScope)
    try {

      // parse this, assuming it's the last XML doc in the string
      parser.parse( new org.xml.sax.InputSource(src), adapter )
      adapter.scopeStack.pop
      adapter.rootElem.asInstanceOf[scala.xml.Node] :: Nil

    } catch {
      case (e : org.xml.sax.SAXParseException) => {
        // we found the start of another xmldoc
        if (e.getMessage != """The processing instruction target matching "[xX][mM][lL]" is not allowed."""
            || adapter.hStack.length != 1 || adapter.hStack(0) == null){
          throw(e)
        }

        // tell the adapter we reached the end of a document
        adapter.endDocument

        // grab the current root node
        adapter.scopeStack.pop
        val node = adapter.rootElem.asInstanceOf[scala.xml.Node]

        // reset to the start of this doc
        src.relocate(e.getLineNumber, e.getColumnNumber, -6)

        // and parse the next doc
        node :: parse( src )
      }
    }
  }
}

println(ConcatenatedXML.parse(new java.io.BufferedReader(
  new java.io.FileReader("temp.xml")
)))
println(ConcatenatedXML.parse(
  """|<?xml version="1.0" encoding="UTF-8"?>
     |<firstDoc><inner><innerer><innermost></innermost></innerer></inner></firstDoc>
     |<?xml version="1.0" encoding="UTF-8"?>
     |<secondDoc></secondDoc>
     |<?xml version="1.0" encoding="UTF-8"?>
     |<thirdDoc>...</thirdDoc>
     |<?xml version="1.0" encoding="UTF-8"?>
     |<lastDoc>...</lastDoc>""".stripMargin
))
try {
  ConcatenatedXML.parse(
    """|<?xml version="1.0" encoding="UTF-8"?>
       |<firstDoc>
       |<?xml version="1.0" encoding="UTF-8"?>
       |</firstDoc>""".stripMargin
  )
  throw(new Exception("That should have failed"))
} catch {
  case _ => println("catches really incomplete docs")
}
于 2012-04-18T20:44:58.143 に答える