Изменение пространства имен XML с Scala

Мне лично нравится использовать схему, которая фокусируется на уровне назад совместимости, которую могут ожидать пользователи проекта/продукта:

Прежде 1.0:

  • 0.0.1 = Первый выпуск
  • , 0.-.X = Назад совместимое обновление
  • 0. X.0 = Назад несовместимое обновление

После 1.0:

  • -.-.X = Обновление без интерфейсных изменений
  • -.X.0 = Обновление с назад совместимыми интерфейсными дополнениями
  • X.0.0 = Назад несовместимое обновление

Используя совместимость как центральная точка в номере версии облегчает для пользователей, особенно если te продуктом является библиотека, чтобы судить, могут ли они ожидать smoothe и безопасный обновление или нет.

5
задан toddk 7 July 2009 в 20:12
поделиться

2 ответа

Вот оно. Поскольку NamespaceBinding является вложенным (у каждого ns есть родительский элемент, кроме TopScope), нам нужно выполнить рекурсию, чтобы исправить это. Кроме того, у каждого ns есть URI и префикс, и нам нужно изменить оба.

Функция ниже изменит только один конкретный URI и префикс, и она проверит все пространства имен, чтобы увидеть, нужно ли изменить префикс или URI. Он изменит префикс или URI независимо друг от друга, что может быть не тем, что нужно. Хотя это не большая проблема.

Что касается остального, просто сопоставление с образцом на Elem для рекурсии в каждую часть XML. А, да, префикс элементов тоже меняет. Опять же, если это не то, что нужно, это легко изменить.

Код предполагает, что нет необходимости рекурсивно переходить к «другим» частям XML - остальные, как правило, являются текстовыми элементами. Также, предполагается, что в другом месте нет пространства имен. Я не специалист по XML, поэтому могу ошибаться в обоих пунктах. И снова, это должно быть легко изменить - просто следуйте шаблону.

def changeNS(el: Elem, 
             oldURI: String, newURI: String, 
             oldPrefix: String, newPrefix: String): Elem = {
  def replace(what: String, before: String, after: String): String =
    if (what == before) after else what

  def fixScope(ns: NamespaceBinding): NamespaceBinding =
    if(ns == TopScope)
      TopScope
    else new NamespaceBinding(replace(ns.prefix, oldPrefix, newPrefix),
                              replace(ns.uri, oldURI, newURI),
                              fixScope(ns.parent))

  def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match {
    case Elem(prefix, label, attribs, scope, children @ _*) => 
      Elem(replace(prefix, oldPrefix, newPrefix), 
           label, 
           attribs, 
           fixScope(scope), 
           fixSeq(children) : _*)
    case other => other
  }
  fixSeq(el.theSeq)(0).asInstanceOf[Elem]
}

Однако это дает неожиданный результат. Область действия добавляется ко всем элементам. Это потому, что NamespaceBinding не определяет метод equals, поэтому использует ссылочное равенство. Я открыл для этого заявку 2138 , которая уже закрыта, поэтому у Scala 2.8 не будет этой проблемы.

Между тем, следующий код будет работать правильно. Он хранит кеш пространств имен. Он также раскладывает NamespaceBinding в список перед его обработкой.

  def changeNS(el: Elem, 
             oldURI: String, newURI: String, 
             oldPrefix: String, newPrefix: String): Elem = {
  val namespaces = scala.collection.mutable.Map.empty[List[(String, String)],NamespaceBinding]

  def replace(what: String, before: String, after: String): String =
    if (what == before) after else what

  def unfoldNS(ns: NamespaceBinding): List[(String, String)] = ns match {
    case TopScope => Nil
    case _ => (ns.prefix, ns.uri) :: unfoldNS(ns.parent)
    }

  def foldNS(unfoldedNS: List[(String, String)]): NamespaceBinding = unfoldedNS match {
    case knownNS if namespaces.isDefinedAt(knownNS) => namespaces(knownNS)
    case (prefix, uri) :: tail =>
      val newNS = new NamespaceBinding(prefix, uri, foldNS(tail))
      namespaces(unfoldedNS) = newNS
      newNS
    case Nil => TopScope
  }

  def fixScope(ns: NamespaceBinding): NamespaceBinding =
    if(ns == TopScope)
      ns
    else {
      val unfoldedNS = unfoldNS(ns)
      val fixedNS = for((prefix, uri) <- unfoldedNS) 
                    yield (replace(prefix, oldPrefix, newPrefix), replace(uri, oldURI, newURI))

      if(!namespaces.isDefinedAt(unfoldedNS))
        namespaces(unfoldedNS) = ns  // Save for future use

      if(fixedNS == unfoldedNS)
        ns
      else 
        foldNS(fixedNS)
    }

  def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match {
    case Elem(prefix, label, attribs, scope, children @ _*) => 
      Elem(replace(prefix, oldPrefix, newPrefix), 
           label, 
           attribs, 
           fixScope(scope), 
           fixSeq(children) : _*)
    case other => other
  }
  fixSeq(el.theSeq)(0).asInstanceOf[Elem]
}
9
ответ дан 14 December 2019 в 01:14
поделиться

Небольшая ошибка. Атрибуты также могут иметь уточненные имена. Их тоже нужно проверить.

0
ответ дан 14 December 2019 в 01:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: