Yaidom: a Scala XML query and transformation API (Apache 2.0 license)
Showing yaidom by examples using XBRL
						
Created by chris.de.vreeze@ebpi.nl
					
						
					
						
Powered by reveal.js
					
<xbrli:xbrl xmlns:xbrli="http://www.xbrl.org/2003/instance"
xmlns:cc2-i="cc2i" xmlns:cc-t="cct" xmlns:cd="nlcd" xmlns:iso4217="iso4217">
<xbrli:context id="FY14d">
<xbrli:entity>
<xbrli:identifier scheme="http://www.cc.eu/cc-id">30267975
</xbrli:identifier>
</xbrli:entity>
<xbrli:period>
<xbrli:startDate>2014-01-01</xbrli:startDate>
<xbrli:endDate>2014-12-31</xbrli:endDate>
</xbrli:period>
</xbrli:context>
<xbrli:unit id="EUR">
<xbrli:measure>iso4217:EUR</xbrli:measure>
</xbrli:unit>
<cc2-i:Equity contextRef="FY14d" unitRef="EUR"
decimals="INF">95000</cc2-i:Equity>
<cc-t:EntityAddressPresentation>
<cd:POBoxNumber contextRef="FY14d">2312</cd:POBoxNumber>
<cd:PostalCodeNL contextRef="FY14d">2501CD</cd:PostalCodeNL>
<cd:PlaceOfResidenceNL contextRef="FY14d">Den Haag
</cd:PlaceOfResidenceNL>
<cd:CountryName contextRef="FY14d">Nederland</cd:CountryName>
</cc-t:EntityAddressPresentation>
</xbrli:xbrl>
First some yaidom basics:
Below methods "filter" and "map" are shown:
							
val xbrliNs = "http://www.xbrl.org/2003/instance"
val contexts =
  instance.findAllChildElems.filter(e =>
    e.resolvedName == EName(xbrliNs, "context"))
val contextIds =
  contexts.map(e => e.attribute(EName("id")))
							
						
					<xbrli:xbrl xmlns:xbrli="http://www.xbrl.org/2003/instance"
xmlns:cc2-i="cc2i" xmlns:cc-t="cct" xmlns:cd="nlcd" xmlns:iso4217="iso4217">
<xbrli:context id="FY14d">
<xbrli:entity>
<xbrli:identifier scheme="http://www.cc.eu/cc-id">30267975
</xbrli:identifier>
</xbrli:entity>
<xbrli:period>
<xbrli:startDate>2014-01-01</xbrli:startDate>
<xbrli:endDate>2014-12-31</xbrli:endDate>
</xbrli:period>
</xbrli:context>
<xbrli:unit id="EUR">
<xbrli:measure>iso4217:EUR</xbrli:measure>
</xbrli:unit>
<cc2-i:Equity contextRef="FY14d" unitRef="EUR"
decimals="INF">95000</cc2-i:Equity>
<cc-t:EntityAddressPresentation>
<cd:POBoxNumber contextRef="FY14d">2312</cd:POBoxNumber>
<cd:PostalCodeNL contextRef="FY14d">2501CD</cd:PostalCodeNL>
<cd:PlaceOfResidenceNL contextRef="FY14d">Den Haag
</cd:PlaceOfResidenceNL>
<cd:CountryName contextRef="FY14d">Nederland</cd:CountryName>
</cc-t:EntityAddressPresentation>
</xbrli:xbrl>
Finding facts, contexts and units (as plain XML elements), regardless of the element implementation:
val ns = "http://www.xbrl.org/2003/instance"
val linkNs = "http://www.xbrl.org/2003/linkbase"
def hasCustomNs(e: Elem): Boolean = {
  !Set(Option(ns), Option(linkNs)).contains(
    e.resolvedName.namespaceUriOption)
}
val contexts = xbrlInstance.filterChildElems(withEName(ns, "context"))
val units = xbrlInstance.filterChildElems(withEName(ns, "unit"))
val topLevelFacts =
  xbrlInstance.filterChildElems(e => hasCustomNs(e))
val nestedFacts =
  topLevelFacts.flatMap(_.filterElems(e => hasCustomNs(e)))
val allFacts =
  topLevelFacts.flatMap(_.filterElemsOrSelf(e => hasCustomNs(e)))
							
						
					Non-trivial queries combine facts with their contexts and units:
val contextsById =
  contexts.groupBy(_.attribute(EName("id")))
val unitsById =
  units.groupBy(_.attribute(EName("id")))
// Use these Maps to look up contexts and units from
// (item) facts, with predictable performance ...
							
						
					<xbrli:xbrl xmlns:xbrli="http://www.xbrl.org/2003/instance"
xmlns:cc2-i="cc2i" xmlns:cc-t="cct" xmlns:cd="nlcd" xmlns:iso4217="iso4217">
<xbrli:context id="FY14d">
<xbrli:entity>
<xbrli:identifier scheme="http://www.cc.eu/cc-id">30267975
</xbrli:identifier>
</xbrli:entity>
<xbrli:period>
<xbrli:startDate>2014-01-01</xbrli:startDate>
<xbrli:endDate>2014-12-31</xbrli:endDate>
</xbrli:period>
</xbrli:context>
<xbrli:unit id="EUR">
<xbrli:measure>iso4217:EUR</xbrli:measure>
</xbrli:unit>
<cc2-i:Equity contextRef="FY14d" unitRef="EUR"
decimals="INF">95000</cc2-i:Equity>
<cc-t:EntityAddressPresentation>
<cd:POBoxNumber contextRef="FY14d">2312</cd:POBoxNumber>
<cd:PostalCodeNL contextRef="FY14d">2501CD</cd:PostalCodeNL>
<cd:PlaceOfResidenceNL contextRef="FY14d">Den Haag
</cd:PlaceOfResidenceNL>
<cd:CountryName contextRef="FY14d">Nederland</cd:CountryName>
</cc-t:EntityAddressPresentation>
</xbrli:xbrl>
// All namespace declarations must be in the root element
require(
  xbrlInstance.findAllElems.forall(_.scope == xbrlInstance.scope))
							
							
val standardScope = Scope.from(
  "xbrli" -> "http://www.xbrl.org/2003/instance",
  "xlink" -> "http://www.w3.org/1999/xlink",
  "link" -> "http://www.xbrl.org/2003/linkbase",
  "xsi" -> "http://www.w3.org/2001/XMLSchema-instance",
  "iso4217" -> "http://www.xbrl.org/2003/iso4217")
val standardPrefixes = standardScope.keySet
val standardNamespaceUris = standardScope.inverse.keySet
							
							
val subscope = xbrlInstance.scope.withoutDefaultNamespace filter {
  case (pref, ns) =>
    standardPrefixes.contains(pref) ||
      standardNamespaceUris.contains(ns)
}
require(subscope.subScopeOf(standardScope)) // fails on iso4217
							
						
					
val ns = "http://www.xbrl.org/2003/instance"
val linkNs = "http://www.xbrl.org/2003/linkbase"
def hasCustomNs(e: Elem): Boolean = {
  !Set(Option(ns), Option(linkNs)).contains(
    e.resolvedName.namespaceUriOption)
}
val contexts = xbrlInstance.filterChildElems(withEName(ns, "context"))
val units = xbrlInstance.filterChildElems(withEName(ns, "unit"))
val topLevelFacts =
  xbrlInstance.filterChildElems(e => hasCustomNs(e))
val allFacts =
  topLevelFacts.flatMap(_.filterElemsOrSelf(e => hasCustomNs(e)))
							
							
val contextIds =
  contexts.map(_.attribute(EName("id"))).toSet
val usedContextIds =
  allFacts.flatMap(_.attributeOption(EName("contextRef"))).toSet
require(usedContextIds.subsetOf(contextIds))
require(contextIds.subsetOf(usedContextIds))
							
						
					Expressive and type-safe validation code, using an imaginary yaidom extension for XBRL instances:
val contextIds = xbrlInstance.allContextsById.keySet
val usedContextIds =
  xbrlInstance.findAllItems.map(_.contextRef).toSet
require(usedContextIds.subsetOf(contextIds))
require(contextIds.subsetOf(usedContextIds))
							
						
					
						
Yaidom (Apache 2.0 license) can be found at https://github.com/dvreeze/yaidom