使用scala.xml.transform.RuleTransformer基于其兄弟转换XML元素

时间:2013-05-14 14:42:47

标签: xml scala pattern-matching

鉴于此XML:

<root>
 <item>
   <discriminator>d1</discriminator>
   <target>t1</target>
   <subitem>
       <incomplete></incomplete>
   </subitem>
   <subitem>
       <incomplete></incomplete>
   </subitem>
 </item>
 <item>
    <discriminator>d2</discriminator>
   <target>t2</target>
   <subitem>
       <incomplete></incomplete>
   </subitem>
 </item>
</root>

我需要改变它: 1)对于每个<item><target>的文字会根据<discriminator>中的文字进行修改。
2)为每个<incomplete>添加一些文本内容。

基于其他SO帖子,到目前为止,我已经提出了2)的解决方案,使用RewriteRuleRuleTransformer。它是这样的:

object completeIncomplete extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case Elem(_, "incomplete", _, _, _*) =>
          <incomplete>content</incomplete>
        case other => other
      }
    }
object transform extends RuleTransformer(completeIncomplete)
transform(xml)

这似乎工作正常,但我不知道:

  • 如何实现1)(基于其兄弟修改元素)
  • 如何组合两个处理步骤。那就是:在这种情况下,有两个不同的RewriteRule-s是否有意义?这两个是浪费吗?是否有可能/建议在单次迭代中实现两种转换?

对于1),我试图模仿匹配孩子的列表,如下所示:

case Elem("", "item", _, _, disc@Elem("", "discriminator", _, _, _*)) 
    if discriminate(disc) => //...?

或者这个:

case item@Elem("", "item", _, _, _*) 
    if discriminate(item \ "disc") => //..?

但是这样做并没有那么好,因为我不知道如何仅用替换<target>来重新创建整个项目

我甚至不知道在这种情况下是否可以与<item>匹配。但如果是的话,我能以某种方式实现其<incomplete>孩子的转变吗?

这里的正确方法是什么?

1 个答案:

答案 0 :(得分:1)

看看这是否适合你:

  override def transform(n: Node): Seq[Node] = n match {

    case el @ Elem(_, "item", _, _, _*) =>
      val children = el.child
      val disc = children.find(_.label == "discriminator")
      val content = children.collect{
        case el @ Elem(_, "target", _, _, _*) =>
          <target>{el.text + disc.map(_.text).getOrElse("")}</target>
        case e:Elem => e
      }
      <item>{content}</item>

    case el @ Elem(_, "incomplete", _, _, _*) =>  
      <incomplete>some content</incomplete>
    case other => other
  }

xml结构是不可变的,因此当您使用特定元素时,您无法访问它的父级。因此,我选择通过停在item上并替换其子内容来解决问题#1。我希望这就是你要找的东西。