XPath

XPath (XML Path Language) — язык запросов к элементам XML-документа.

XQuery – расширение XPath (итерация, и др.)

https://www.freeformatter.com/xpath-tester.html - для тестирования xpath запросов

https://fedstat.ru/

Обрабатываемые данные

Обрабатываемые данные

Обрабатываемые данные

Инструменты

private val parser =
    XPathFactory
        .newInstance()
        .newXPath()

private val doc =
  DocumentBuilderFactory
    .newInstance()
    .newDocumentBuilder()
    .parse(Reader::class.java
      .getResourceAsStream("data_part.xml"))

Обработка

val result = parser
  .compile(query)
  .evaluate(doc, XPathConstants.NODESET) as NodeList
result.map(cursorQuery).also {
  print(it)
}
println()

fun <R> NodeList.map(transform: (Node) -> R): List<R> =

Выбор узлов по имени

val query = "//CodeList/Name"
val cursorQuery = { node: Node ->
  node.firstChild.nodeValue
}

[Классификатор видов экономической деятельности (ОКВЭД2), 
Классификатор объектов административно-территориального деления 
(ОКАТО)]

Оси

  • child::
  • descendant:: (/descendant::node()/ = //)
  • parent:: (...)
  • ancestor::
  • self:: (.)
  • attribute:: (@)
  • и др.

Функции над узлами

  • node() (*)
  • text()
  • count(node-set)
  • строковые
  • логические

Дополнительные условия выбора

query = 
  "//CodeList[@id='s_OKATO']/Code/Description[contains(.,'мск')]"
cursorQuery = 
  node.firstChild.nodeValue + " - " +
  node.parentNode.attributes.getNamedItem("value").nodeValue

[Костромская область - 34000000000, Омская область - 52000000000, 
Томская область - 69000000000, Пермский край - 57000000000]

Анализ узлов с помощью XPath

fun Node.getText(query: String) =
    parser
        .compile(query)
        .evaluate(this)
        .toString()
query = 
  "//CodeList[@id='s_OKATO']/Code/Description[contains(.,'мск')]"
cursorQuery = 
  node.getText("../@value") + ": " + node.getText(".")

[34000000000: Костромская область, 52000000000: Омская область, 
  69000000000: Томская область, 57000000000: Пермский край]

Пример анализа данных

val cursorQuery = { node: Node ->
  val year = node.getText("../..//Time")
  val month = node
    .getText("../..//Value[@concept='PERIOD']/@value")
  val value = node.getText("../..//ObsValue/@value")
  "$month $year: $value\n"
}
val query = "//SeriesKey/Value[@value='643']"

[январь 2018: 80078,3
, февраль 2018: 79768,34
, январь-февраль 2018: 79880,03
, март 2018: 86360,83
, январь-март 2018: 82056,33
...

Пример анализа данных

val query = "//SeriesKey/Value[@value='643']
  [../../Attributes/Value
    [@concept='PERIOD' and not(contains(@value, '-'))]
  ]"

[январь 2018: 80078,3
, февраль 2018: 79768,34
, март 2018: 86360,83
, апрель 2018: 81437,37
, май 2018: 78942,09
...
, январь 2018: 80078,3
, февраль 2018: 79768,34
, март 2018: 86360,83
...

Пример анализа данных

val query = "//SeriesKey/Value[@concept='s_OKATO' and @value='643']
  [../Value[@concept='s_OKVED2' and @value='62']]
  [../../Attributes/Value
    [@concept='PERIOD' and not(contains(@value, '-'))]]"

[январь 2018: 80078,3
, февраль 2018: 79768,34
, март 2018: 86360,83
, апрель 2018: 81437,37
, май 2018: 78942,09
, июнь 2018: 184905,6
, июль 2018: 78829,66
...