How to change attribute on Scala XML Element

后端 未结 5 794
渐次进展
渐次进展 2020-12-13 04:41

I have an XML file that I would like to map some attributes of in with a script. For example:


  
         


        
5条回答
  •  心在旅途
    2020-12-13 05:35

    Ok, best effort, Scala 2.8. We need to reconstruct attributes, which means we have to decompose them correctly. Let's create a function for that:

    import scala.xml._
    
    case class GenAttr(pre: Option[String], 
                       key: String, 
                       value: Seq[Node], 
                       next: MetaData) {
      def toMetaData = Attribute(pre, key, value, next)
    }
    
    def decomposeMetaData(m: MetaData): Option[GenAttr] = m match {
      case Null => None
      case PrefixedAttribute(pre, key, value, next) => 
        Some(GenAttr(Some(pre), key, value, next))
      case UnprefixedAttribute(key, value, next) => 
        Some(GenAttr(None, key, value, next))
    }
    

    Next, let's decompose the chained attributes into a sequence:

    def unchainMetaData(m: MetaData): Iterable[GenAttr] = 
      m flatMap (decomposeMetaData)
    

    At this point, we can easily manipulate this list:

    def doubleValues(l: Iterable[GenAttr]) = l map {
      case g @ GenAttr(_, _, Text(v), _) if v matches "\\d+" => 
        g.copy(value = Text(v.toInt * 2 toString))
      case other => other
    }
    

    Now, chain it back again:

    def chainMetaData(l: Iterable[GenAttr]): MetaData = l match {
      case Nil => Null
      case head :: tail => head.copy(next = chainMetaData(tail)).toMetaData
    }
    

    Now, we only have to create a function to take care of these things:

    def mapMetaData(m: MetaData)(f: GenAttr => GenAttr): MetaData = 
      chainMetaData(unchainMetaData(m).map(f))
    

    So we can use it like this:

    import scala.xml.transform._
    
    val attribs = Set("attr1", "attr2")
    val rr = new RewriteRule {
      override def transform(n: Node): Seq[Node] = (n match {
        case e: Elem =>
          e.copy(attributes = mapMetaData(e.attributes) {
            case g @ GenAttr(_, key, Text(v), _) if attribs contains key =>
              g.copy(value = Text(v.toInt * 2 toString))
            case other => other
          })
        case other => other
      }).toSeq
    }
    val rt = new RuleTransformer(rr)
    

    Which finally let you do the translation you wanted:

    rt.transform()
    

    All of this could be simplified if:

    • Attribute actually defined prefix, key and value, with an optional prefix
    • Attribute was a sequence, not a chain
    • Attribute had a map, mapKeys, mapValues
    • Elem had a mapAttribute

提交回复
热议问题