Я понятия не имел, как это сделать, но Пангея показала мне путь .Сначала давайте создадим черту для обработки местоположения:
import org.xml.sax.{helpers, Locator, SAXParseException}
trait WithLocation extends helpers.DefaultHandler {
var locator: org.xml.sax.Locator = _
def printLocation(msg: String) {
println("%s at line %d, column %d" format (msg, locator.getLineNumber, locator.getColumnNumber))
}
// Get location
abstract override def setDocumentLocator(locator: Locator) {
this.locator = locator
super.setDocumentLocator(locator)
}
// Display location messages
abstract override def warning(e: SAXParseException) {
printLocation("warning")
super.warning(e)
}
abstract override def error(e: SAXParseException) {
printLocation("error")
super.error(e)
}
abstract override def fatalError(e: SAXParseException) {
printLocation("fatal error")
super.fatalError(e)
}
}
Далее, давайте создадим наш собственный загрузчик, переопределяющий XMLLoader
adapter
, чтобы включить нашу черту:
import scala.xml.{factory, parsing, Elem}
object MyLoader extends factory.XMLLoader[Elem] {
override def adapter = new parsing.NoBindingFactoryAdapter with WithLocation
}
Иэто все, что нужно сделать!Объект XML
добавляет немного к XMLLoader
- в основном, к методам save
.Возможно, вы захотите взглянуть на его исходный код, если вы чувствуете необходимость полной замены.Но это только в том случае, если вы хотите обработать все это самостоятельно, так как Scala уже имеет черту, приводящую к ошибкам:
object MyLoader extends factory.XMLLoader[Elem] {
override def adapter = new parsing.NoBindingFactoryAdapter with parsing.ConsoleErrorHandler
}
Черта ConsoleErrorHandler
извлекает информацию о строках и числах из исключения с помощьюпуть.Для наших целей нам нужно расположение и вне исключений (я предполагаю).
Теперь, чтобы изменить само создание узла, рассмотрим абстрактные методы scala.xml.factory.FactoryAdapter
.Я остановился на createNode
, но переопределяю на уровне NoBindingFactoryAdapter
, потому что он возвращает Elem
вместо Node
, что позволяет мне добавлять атрибуты.Итак:
import org.xml.sax.Locator
import scala.xml._
import parsing.NoBindingFactoryAdapter
trait WithLocation extends NoBindingFactoryAdapter {
var locator: org.xml.sax.Locator = _
// Get location
abstract override def setDocumentLocator(locator: Locator) {
this.locator = locator
super.setDocumentLocator(locator)
}
abstract override def createNode(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, children: List[Node]): Elem = (
super.createNode(pre, label, attrs, scope, children)
% Attribute("line", Text(locator.getLineNumber.toString), Null)
% Attribute("column", Text(locator.getColumnNumber.toString), Null)
)
}
object MyLoader extends factory.XMLLoader[Elem] {
// Keeping ConsoleErrorHandler for good measure
override def adapter = new parsing.NoBindingFactoryAdapter with parsing.ConsoleErrorHandler with WithLocation
}
Результат:
scala> MyLoader.loadString("<a><b/></a>")
res4: scala.xml.Elem = <a line="1" column="12"><b line="1" column="8"></b></a>
Обратите внимание, что он получил последнее местоположение, которое находится в закрывающем теге.Это одна вещь, которую можно улучшить, переопределив startElement
, чтобы отслеживать, где каждый элемент начал в стеке, и endElement
, чтобы выскочить из этого стека в var
, используемый createNode
.
Хороший вопросЯ многому научился!: -)