Scala - XML-преобразование отбрасывает преобразованные элементы с несколькими атрибутами - PullRequest
1 голос
/ 30 ноября 2010

Немного странно, вот это, и я испытываю желание предположить, что это может быть ошибкой.Фон - у меня есть целая загрузка XML, которая выглядит следующим образом:

<foo id="1">
 <bar id="0">
  <baz id="0" blah="blah" etc="etc">
   <buz id="0" />
  </baz>
  <buz id="0" blah="blah" etc="etc">
   ...
  </buz>
 </bar>
</foo>
<foo id="2">
 <bar id="0">
  <baz id="0" blah="blah" etc="etc">
   <buz id="0" />
  </baz>
  <buz id="0" blah="blah" etc="etc">
   ...
  </buz>
 </bar>
</foo>
....

Я хочу преобразовать это так, чтобы для каждого элемента foo я заменял все нулевые идентификаторы внутри него наid foo.

Для этого - во-первых, я использую код Даниэля вместе с неявным преобразованием для предоставления 'Elem' метода mapAttributes:

class ElemWithUsefulAttributes(elem : Elem) extends Elem(elem.prefix, elem.label, elem.attributes, elem.scope, elem.child : _*) {
def mapAttributes(f : GenAttr => GenAttr) = this.copy(attributes = mapMetaData(elem.attributes)(f))
}

implicit def Elem2ElemWithUsefulAttributes(elem : Elem) = new ElemWithUsefulAttributes(elem)

Затем у меня есть метод replaceId:

def replaceId(attr : String, id : String)(in : GenAttr) = in match {
  case g@GenAttr(_,key,Text(v),_) if (key.equals(attr)) => g.copy(value=Text(id))
  case other => other
}

Наконец, я создаю пару RewriteRule с и соответствующих RuleTransformer с, чтобы справиться с этим:

class rw1(id : String) extends RewriteRule {
  override def transform(n : Node) : Seq[Node] = n match {
    case n2: Elem if (n2.label == "bar") => n2.mapAttributes(replaceId("id", id))
    case n2: Elem if (n2.label == "baz") => n2.mapAttributes(replaceId("id", id))
    case n2: Elem if (n2.label == "buz") => n2.mapAttributes(replaceId("id", id))
    case other => other
  }
}
class rt1(id : String) extends RuleTransformer(new rw1(id))
object rw2 extends RewriteRule {
  override def transform(n : Node) : Seq[Node] = n match {
    case n2@Elem(_, "foo", _, _, _*) => (new rw1(n2.attribute("id").get.toString))(n2)
    case other => other
  }
}
val rt2 = new RuleTransformer(rw2)

После вызова rt2(xml) я получаю вывод, который выглядит следующим образом:

<foo id="1">
 <bar id="1">
  <baz id="0" blah="blah" etc="etc">
   <buz id="1" />
  </baz>
  <buz id="0" blah="blah" etc="etc">
   ...
  </buz>
 </bar>
</foo>
<foo id="2">
 <bar id="2">
  <baz id="0" blah="blah" etc="etc">
   <buz id="2" />
  </baz>
  <buz id="0" blah="blah" etc="etc">
   ...
  </buz>
 </bar>
</foo>
....

Другими словами, атрибуты не были изменены, когда имеется несколько атрибутов.Естественно, хочется предположить, что это проблема с кодом mapAttributes - однако, если я выгружу результаты преобразования в операторе 'match' в rw1, я отчетливо вижу, что он показывает:

  <baz id="2" blah="blah" etc="etc">
   <buz id="2" />
  </baz>

Далее, если я изменю один базис, скажем, чтобы удалить дополнительные атрибуты, тогда он будет работать правильно и будет отображен.Так что это кажется странным сочетанием множества атрибутов и преобразований.

Кто-нибудь может сделать из этого голову или хвост?

Ответы [ 2 ]

0 голосов
/ 02 декабря 2010

Разве вы не должны обернуть new rw1 в new RuleTransformer? В противном случае я думаю, что оно будет применять правило только к узлу foo, а не к его дочерним элементам.

Может быть, это само по себе решит проблему. Если это не так, вот другой код, который, кажется, делает то, что вы хотите (используя мой подход к изменению атрибутов ):

class ReplaceIDsBelowFooRule(id: String) extends RewriteRule {
  override def transform(n : Node) : Seq[Node] = n match {
    case elem: Elem if elem.label == "foo" =>
      elem.copy(child=this.replaceDescendantIDs(elem.child))
    case other => other
  }
  def replaceDescendantIDs(nodes: Seq[Node]): Seq[Node] = {
    for (node <- nodes) yield node match {
      case elem: Elem => elem.copy(
        child=this.replaceDescendantIDs(elem.child),
        attributes=
          for (attr <- elem.attributes) yield attr match {
            case attr@Attribute("id", _, _) => attr.goodcopy(value=this.id)
            case other => other
          }
      )
      case other => other
    }
  }
}

И применяется к вашему xml:

scala> new RuleTransformer(new ReplaceIDsBelowFooRule("XXX"))(xml)
res1: scala.xml.Node = 
<xml>
<foo id="1">
 <bar id="XXX">
  <baz blah="blah" etc="etc" id="XXX">
   <buz id="XXX"></buz>
  </baz>
  <buz blah="blah" etc="etc" id="XXX">
   ...
  </buz>
 </bar>
</foo>
<foo id="2">
 <bar id="XXX">
  <baz blah="blah" etc="etc" id="XXX">
   <buz id="XXX"></buz>
  </baz>
  <buz blah="blah" etc="etc" id="XXX">
   ...
  </buz>
 </bar>
</foo>
</xml>
0 голосов
/ 30 ноября 2010

Проблема может быть здесь:

case other => other

Вместо этого попробуйте:

case elem: Elem => elem.copy(child = transform(elem.child))
case other => other

Мне интересно, не разбилась ли библиотека между 2.7 и 2.8, но преобразование неВводить автоматически в каждого ребенка.Когда-нибудь я должен это посмотреть (хотя, конечно, приветствуются и другие глаза:).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...