Как создать динамические атрибуты xml через карту в scala

У меня есть одна карта, которая содержит некоторые атрибуты, и я хотел бы создать элемент xml с этими атрибутами.

например config — это карта scala, и я хочу использовать ее следующим образом, но на самом деле следующее не работает. Кто-нибудь может мне помочь? Спасибо

<input type="text" {config.map(entry => entry._1 + "=" + entry._2)}></input>

person zjffdu    schedule 03.05.2017    source источник


Ответы (2)


Используйте низкоуровневый API. Это то, что генерирует парсер scalac при обезуглероживании ваших литералов xml:

import scala.xml.{UnprefixedAttribute, MetaData, TopScope, Text, Elem, Null}

val config: Map[String, String] = Map("k1" -> "v2", "k2" -> "v2")

val seed: MetaData = Null // Don't ask.
val meta: MetaData = config.toList.foldLeft(seed) {
  case (acc, (s1, s2)) =>
    new UnprefixedAttribute(
      key   = s1,
      value = s2,
      next  = acc
    )
}

Elem(
  prefix        = null,
  label         = "input",
  attributes    = meta,
  scope         = TopScope,
  minimizeEmpty = false
)
person OlivierBlanvillain    schedule 03.05.2017
comment
Это много грязного белья. Может быть, Scala сможет разобрать элемент input, а затем добавить атрибуты? Такой код может быть проще. - person ashawley; 04.05.2017
comment
Действительно, ваше решение намного чище :). Я хотел подчеркнуть, что можно понять API-интерфейсы scala.xml и использовать их напрямую. Несмотря на несколько странных углов, они довольно просты и хорошо документированы. - person OlivierBlanvillain; 05.05.2017
comment
Да, это правда. Глядя на это какое-то время, я думаю, что это не так сложно, как я думал сначала. - person ashawley; 05.05.2017

Облом вы не можете написать то, что вы хотели. Я предполагаю, что синтаксический анализатор литералов XML компилятора Scala недостаточно сложен, чтобы анализировать литералы XML с выражениями, только что вставленными где угодно. Они могут быть только между тегами или как значения отдельных атрибутов.

В качестве обходного пути можно создать элемент без атрибутов или только с одним атрибутом, а остальные отсутствуют:

val elem = <input type="text"/>

Затем вы можете добавить атрибуты из вашего Map с помощью пары конструкторов копирования.

elem.copy(
  attributes = elem.attributes.copy(
    config.foldLeft(scala.xml.Null: scala.xml.MetaData) {
      case (next, (k, v)) => scala.xml.Attribute(k, scala.xml.Text(v), next)
    }
))
res1: scala.xml.Elem = <input type="text" k2="v2" k1="v2"/>

В приведенном выше примере используется конструктор копирования для Elem, а затем один для attributes.

Также есть возможность просто использовать один конструктор копирования и свернуть немного по-другому:

elem.copy(attributes = config.foldRight(elem.attributes) {
  case ((k, v), next) => scala.xml.Attribute(k, scala.xml.Text(v), next)
})
res2: scala.xml.Elem = <input k1="v2" k2="v2" type="text"/>

Это может быть самый простой вариант.

Хотя решение Оливье показывает, что UnprefixedAttributed примет String в качестве значения атрибута.

elem.copy(attributes = config.foldRight(elem.attributes) {
  case ((k, v), next) => new scala.xml.UnprefixedAttribute(k, v, next)
})
res3: scala.xml.Elem = <input k1="v2" k2="v2" type="text"/>

Это позволяет избежать необходимости создавать элемент Text, чтобы вы предоставили значение NodeSeq для Attribute.

person ashawley    schedule 04.05.2017