长时间潜伏,第一次海报。如果我的问题不明确,请告诉我。
我有一个奇怪的XML文件需要解析(将数据放在类中并在内部处理它)。 我提到它很奇怪,因为任何人通常认为应该嵌套,但事实并非如此。让我举个例子:
<root>
<item id="XX" rank="YY">
<top>
<description a="XX"><s>***</s>content<s>+++</s></description>
<mainterm t="XX">term</mainterm>
<description a="YY">more content</description>
</top>
<!-- All examples directly below a data correspond to that data.
Shouldn't they be nested? -->
<data level="10" x="4">data here </data>
<example f="45"> example 1</example>
<example f="12"> example 2</example>
<example f="44"> example 3</example>
<data level="11" x="1">data here </data>
<example f="33"> example 1</example>
<example f="6"> example 2</example>
<example f="18"> example 3</example>
<!-- More data tags with and without examples below -->
</item>
</root>
该文件继续存在数万个项目。有些项目根本不包含数据,而数据标签内部没有任何内容。
我对如何解析这个问题给予了完全的自由,因为我正在努力掌握Scala,所以我选择它来解决这个问题。和过去一样,我使用StAX(Apache AXIOM)进行解析,我在Scala中寻找类似的东西,我找到了Scales。到现在为止还挺好。
使用:
我需要的不仅是每个标签的内容和属性,还包括每个标签的原始内容。例如,在上面的XML中,对于“top”标记,我会有以下内容:
case class Top(descriptions:List[Description], key: Key, rawContent: String)
rawContent将是:
<description a="XX"><s>***</s>content<s>+++</s></description>
<mainterm t="XX">term</mainterm>
<description a="YY">more content</description
同样适用于“data”标签,但由于数据没有嵌套,并且假设拉解析为您提供了一个XmlPull,它只是一个Iterator [PullType],我想到了解析标签的想法,遍历节点,直到找到结束标记,或者在“data”标记的情况下,直到我找到另一个“数据”开始标记或“项目”结束标记。但是,无论我如何看待这个问题,我都无法避免挽救状态。
我决定尝试拉链。
首先,因为我需要遍历,直到找到一个给定的标签并同时对我发现的每个元素做一些事情,我正在尝试使用findBy。下面是我正在尝试的代码。我尝试检索给定标记的属性和其中所有内容的原始内容。
/* Some helpers. Ignore */
class PullTypeValue(pt: PullType) {
private val NUM_DEL_CHARS = 2
// Tag names are returned like "{}tagname". Getting rid of the first 2 characters
private def stripHeadChars(s: String) = s.substring(NUM_DEL_CHARS)
// Get the tag name or the value of the given PullType
def getNameOrValue = pt match {
case Left(e:Elem) => stripHeadChars(e.name.toString)
case Left(i:XmlItem) => i.value
case Right(e) => stripHeadChars(e.name.toString)
}
}
class PullTypeZipper(z: Zipper[PullType]) {
implicit def toPullTypeValue(e: Elem) = new PullTypeValue(e)
def moveToTag(tag: String) = {
z.findNext(_ match {
case Left(e:Elem) => e.getNameOrValue == tag
case _ => false
})
}
}
implicit def toPulltTypeValue(pt: PullType) = new PullTypeValue(pt)
implicit def toPullTypeValue(e: Elem) = new PullTypeValue(e)
implicit def toPullTypeValue(i: XmlItem) = new PullTypeValue(i)
implicit def toPullTypeValue(e: EndElem) = new PullTypeValue(e)
implicit def toPullTypeZipper(z: Zipper[PullType]) = new PullTypeZipper(z)
/* End of helpers */
/************* Parsing function here *******************/
def parseTag(currentNode: Option[Zipper[PullType]], currentTagName: String) = {
var attrs: Map[String,String] = Map.empty
val ltags = ListBuffer[String]()
val getAttributes = (z: Zipper[PullType]) =>
z.focus match {
case Left(e:Elem) if e.getNameOrValue == currentTagName =>
attrs = e.attributes.map {a => (a.name.toString.substring(2), a.value)}.toMap
ltags += "<" + e.getNameOrValue + ">"
z.next
case Left(e:Elem) =>
ltags += "<" + e.getNameOrValue + ">"
z.next
case Left(t:Text) =>
ltags += t.value
z.next
case Left(i:XmlItem) =>
ltags += i.value
z.next
case Right(e) =>
ltags += "</" + e.getNameOrValue + ">"
(e.getNameOrValue == currentTagName) ? z.some | z.next
}
/* Traverse until finding the close tag for the given tag name
and extract raw contents from each found tag.
Return the zipper with focus on the next element (if any)
*/
val nextNode = currentNode >>= {_.findBy(getAttributes)(_ match {
case Right(e) => e.getNameOrValue == currentTagName
case _ => false
})} >>= {_.next}
(attrs,ltags.mkString(""),nextNode)
}
/************** End of parsing function ************************/
val zipper = pullXml(new FileReader("MyXmlFile.xml")).toStream.toZipper
val (attrs,rawContents,nextNode) = parseTag(zipper >>= {_.moveToTag("top")}, "top")
// Do something with the values...
该代码适用于“top”标记,但如果我使用“item”标记尝试它,我会得到StackOverFlowError:
Exception in thread "main" java.lang.StackOverflowError
at com.ctc.wstx.util.SymbolTable.size(SymbolTable.java:332)
at com.ctc.wstx.util.SymbolTable.mergeChild(SymbolTable.java:291)
at com.ctc.wstx.stax.WstxInputFactory.updateSymbolTable(WstxInputFactory.java:202)
at com.ctc.wstx.sr.BasicStreamReader.close(BasicStreamReader.java:1179)
at scales.xml.XmlPulls$$anon$1.close(XmlPull.scala:134)
at scales.xml.XmlPulls$$anon$1.internalClose(XmlPull.scala:130)
at scales.xml.XmlPull$class.pumpEvent(PullIterator.scala:201)
at scales.xml.XmlPulls$$anon$1.pumpEvent(XmlPull.scala:118)
at scales.xml.XmlPull$class.next(PullIterator.scala:149)
at scales.xml.XmlPulls$$anon$1.next(XmlPull.scala:118)
at scales.xml.XmlPulls$$anon$1.next(XmlPull.scala:118)
at scala.collection.Iterator$class.toStream(Iterator.scala:1143)
at scales.xml.XmlPulls$$anon$1.toStream(XmlPull.scala:118)
at scala.collection.Iterator$$anonfun$toStream$1.apply(Iterator.scala:1143)
at scala.collection.Iterator$$anonfun$toStream$1.apply(Iterator.scala:1143)
at scala.collection.immutable.Stream$Cons.tail(Stream.scala:1085)
at scala.collection.immutable.Stream$Cons.tail(Stream.scala:1077)
at scala.collection.immutable.Stream$$hash$colon$colon$.unapply(Stream.scala:1058)
at scalaz.Zipper$class.next(Zipper.scala:45)
at scalaz.Zippers$$anon$1.next(Zipper.scala:269)
at parser.XMLParser$$anonfun$6$$anonfun$apply$7.apply(XMLParser.scala:258)
at parser.XMLParser$$anonfun$6$$anonfun$apply$7.apply(XMLParser.scala:258)
at scalaz.BooleanW$$anon$1.$bar(BooleanW.scala:142)
at parser.XMLParser$$anonfun$6.apply(XMLParser.scala:258)
at parser.XMLParser$$anonfun$6.apply(XMLParser.scala:237)
at scalaz.Zipper$class.findBy(Zipper.scala:178)
at scalaz.Zippers$$anon$1.findBy(Zipper.scala:269)
at scalaz.Zipper$$anonfun$findBy$1.apply(Zipper.scala:178)
at scalaz.Zipper$$anonfun$findBy$1.apply(Zipper.scala:178)
at scala.Option.flatMap(Option.scala:170)
at scalaz.Bind$$anon$21.bind(Bind.scala:112)
at scalaz.Bind$$anon$21.bind(Bind.scala:111)
at scalaz.MA$class.$greater$greater$eq(MA.scala:73)
at scalaz.MAsLow$$anon$2.$greater$greater$eq(MAB.scala:50)
at scalaz.MASugar$class.$u2217(MA.scala:329)
at scalaz.MAsLow$$anon$2.$u2217(MAB.scala:50)
at scalaz.Zipper$class.findBy(Zipper.scala:178)
at scalaz.Zippers$$anon$1.findBy(Zipper.scala:269)
at scalaz.Zipper$$anonfun$findBy$1.apply(Zipper.scala:178)
at scalaz.Zipper$$anonfun$findBy$1.apply(Zipper.scala:178)
at scala.Option.flatMap(Option.scala:170)
at scalaz.Bind$$anon$21.bind(Bind.scala:112)
at scalaz.Bind$$anon$21.bind(Bind.scala:111)
at scalaz.MA$class.$greater$greater$eq(MA.scala:73)
at scalaz.MAsLow$$anon$2.$greater$greater$eq(MAB.scala:50)
at scalaz.MASugar$class.$u2217(MA.scala:329)
at scalaz.MAsLow$$anon$2.$u2217(MAB.scala:50)
at scalaz.Zipper$class.findBy(Zipper.scala:178)
at scalaz.Zippers$$anon$1.findBy(Zipper.scala:269)
at scalaz.Zipper$$anonfun$findBy$1.apply(Zipper.scala:178)
at scalaz.Zipper$$anonfun$findBy$1.apply(Zipper.scala:178)
... and so on
进行一些研究,并且不确定它是否相关,我发现Scales使用Scalaz 6.0.4,并且在那里,Zipper.findBy不是tailrec,而它是(至少内部函数它)但是如果我将依赖项更改为7.0.4,我会从Scales中获得很多错误,因为Iteratee从Scalaz 6更改为7(某些引用不在同一位置)。
我的问题:
说明: