Tinderbox Forum

Is it possible to parse and manipulate Tinderbox's XML using Ruby?

I am curious to know if anyone here has experience accessing and manipulating notes and notes’ attributes using Ruby – in particular, using the Nokogiri gem (but not necessarily). I started playing around with this but didn’t make it far. The documentation isn’t particularly helpful, so I only managed to access some attributes, such as name and text, but not the other ones. I fear I might not be fully grasping Tbx’s XML structure and how it is supposed to work.

A minimal example:


#!/usr/bin/env ruby
# frozen_string_literal: false

Encoding.default_external = Encoding::UTF_8

require 'nokogiri'

file_path = ""
doc = Nokogiri::XML(File.open(file_path))

tbx = doc.children
items = tbx.xpath('//item')
p items[10] # just a random note, see below the result I am getting.

# attribute values
# items[0].xpath('//attribute').children
Random note
#<Nokogiri::XML::ElementElement:0x410 name="item" attributes=[
#<Nokogiri::XML::ElementAttr:0x3c name="ID" value="1624131850">, 
#<Nokogiri::XML::ElementAttr:0x50 name="Creator" value="Bernardo Vasconcelos">] children=[
#<Nokogiri::XML::ElementText:0x64 "\n">, 
#<Nokogiri::XML::ElementElement:0xa0 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x78 name="name" value="Created">] children=[
#<Nokogiri::XML::ElementText:0x8c "2021-06-19T15:17:51-03:00">]>, 
#<Nokogiri::XML::ElementText:0xb4 "\n">, 
#<Nokogiri::XML::ElementElement:0xf0 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0xc8 name="name" value="DominantLanguage">] children=[
#<Nokogiri::XML::ElementText:0xdc "pt">]>, 
#<Nokogiri::XML::ElementText:0x104 "\n">, 
#<Nokogiri::XML::ElementElement:0x140 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x118 name="name" value="Modified">] children=[
#<Nokogiri::XML::ElementText:0x12c "2021-06-19T15:17:51-03:00">]>, 
#<Nokogiri::XML::ElementText:0x154 "\n">, 
#<Nokogiri::XML::ElementElement:0x190 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x168 name="name" value="MyString">] children=[
#<Nokogiri::XML::ElementText:0x17c "/DA/DA I/DA I 2 Opiniões dos predecessores/Predecessores/">]>, 
#<Nokogiri::XML::ElementText:0x1a4 "\n">, 
#<Nokogiri::XML::ElementElement:0x1e0 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x1b8 name="name" value="NLOrganizations">] children=[
#<Nokogiri::XML::ElementText:0x1cc "Alma">]>, 
#<Nokogiri::XML::ElementText:0x1f4 "\n">, 
#<Nokogiri::XML::ElementElement:0x230 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x208 name="name" value="NLTags">] children=[
#<Nokogiri::XML::ElementText:0x21c "κίνησις">]>, 
#<Nokogiri::XML::ElementText:0x244 "\n">, 
#<Nokogiri::XML::ElementElement:0x280 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x258 name="name" value="Name">] children=[
#<Nokogiri::XML::ElementText:0x26c "Heráclito">]>, 
#<Nokogiri::XML::ElementText:0x294 "\n">, 
#<Nokogiri::XML::ElementElement:0x2d0 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x2a8 name="name" value="SelectionCount">] children=[
#<Nokogiri::XML::ElementText:0x2bc "17">]>, 
#<Nokogiri::XML::ElementText:0x2e4 "\n">, 
#<Nokogiri::XML::ElementElement:0x320 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x2f8 name="name" value="Xpos">] children=[
#<Nokogiri::XML::ElementText:0x30c "4">]>, 
#<Nokogiri::XML::ElementText:0x334 "\n">, 
#<Nokogiri::XML::ElementElement:0x370 name="attribute" attributes=[
#<Nokogiri::XML::ElementAttr:0x348 name="name" value="Ypos">] children=[
#<Nokogiri::XML::ElementText:0x35c "3.5575">]>, 
#<Nokogiri::XML::ElementText:0x384 "\n">, 
#<Nokogiri::XML::ElementElement:0x3ac name="text" children=[
#<Nokogiri::XML::ElementText:0x398 " Alma é o vapor. \n O que está em movimento percebe o que está em movimento. ">]>, 
#<Nokogiri::XML::ElementText:0x3c0 "\n">, 
#<Nokogiri::XML::ElementElement:0x3e8 name="rtfd" children=[
#<Nokogiri::XML::ElementText:0x3d4 "cnRmZAAAAAADAAAAAgAAAAcAAABUWFQucnRmAQAAAC4gAgAAKwAAAAEAAAAYAgAAe1xydGYxXGFu\nc2lcYW5zaWNwZzEyNTJcY29jb2FydGYyNjM2Clxjb2NvYXRleHRzY2FsaW5nMFxjb2NvYXBsYXRm\nb3JtMHtcZm9udHRibFxmMFxmbmlsXGZjaGFyc2V0MCBHZW50aXVtUGx1czt9CntcY29sb3J0Ymw7\nXHJlZDI1NVxncmVlbjI1NVxibHVlMjU1O1xyZWQxOTlcZ3JlZW4xOThcYmx1ZTE4NztccmVkMjU1\nXGdyZWVuMjU1XGJsdWUyNTU7fQp7XCpcZXhwYW5kZWRjb2xvcnRibDs7XGNzcHRocmVlXGM4MTg5\nMVxjODE1ODJcYzc4MzcxO1xjc3B0aHJlZVxjMTAwMDAwXGMxMDAwMDBcYzEwMDAwMDt9ClxwYXJk\nXHR4NTYwXHR4MTEyMFx0eDE2ODBcdHgyMjQwXHR4MjgwMFx0eDMzNjBcdHgzOTIwXHR4NDQ4MFx0\neDUwNDBcdHg1NjAwXHR4NjE2MFx0eDY3MjBccGFyZGlybmF0dXJhbFxwYXJ0aWdodGVuZmFjdG9y\nMAoKXGYwXGZzNDggXGNmMiAgQWxtYSBcJ2U5IG8gdmFwb3IuIApcZnMzMiBcY2YzIFwKClxmczQ4\nIFxjZjIgIE8gcXVlIGVzdFwnZTEgZW0gbW92aW1lbnRvIHBlcmNlYmUgbyBxdWUgZXN0XCdlMSBl\nbSBtb3ZpbWVudG8uIH0BAAAAIwAAAAEAAAAHAAAAVFhULnJ0ZhAAAADXprNhtgEAAAAAAAAAAAAA\n\n">]>, 
#<Nokogiri::XML::ElementText:0x3fc "\n">]>

Salvete,

I found an interesting example. Inspired by this script for R in Zettelkasten compatibility with Markdown apps - #5 by jpl), I managed to extract the main attributes from the notes. I still could not, however, get some of them, such as $Container and $Path.

#!/usr/bin/env ruby
# frozen_string_literal: false

# bcdav 2021-12-11-23-36

Encoding.default_external = Encoding::UTF_8

require 'nokogiri'
require 'terminal-table'

doc = Nokogiri::XML(File.open('path/to/file'))

links = doc.xpath('//link')
links_rows = []

parse_links = true

if parse_links == true
  links.each do |node|
    links_rows << [node.attr('name'), node.attr('sourceid'), node.attr('destid'), node.attr('sstart'), node.attr('slen')]
  end

  headings = %w[name sourceid destid sstart slen]
  links_table = Terminal::Table.new rows: links_rows, headings: headings

  puts links_table

end

# parse_notes = false
parse_notes = true
elems = doc.xpath('//tinderbox//item')
notes_rows = []

if parse_notes == true

  elems.each do |node|
    id = node.attr('ID') || ''
    name = node.at('.//attribute[@name="Name"]').text || ''
    nl_tags = node.at('.//attribute[@name="NLTags"]') ? node.at('.//attribute[@name="NLTags"]').text : ''
    my_string = node.at('.//attribute[@name="MyString"]') ? node.at('.//attribute[@name="MyString"]').text : ''
    text = node.at('//text') ? node.at('//text').text : ''
    prototype = node.attr('proto') || ''
    # p path = node.css_path

    notes_rows << [id, name, nl_tags, my_string, text, prototype]
  end

  puts notes_rows.join(';')

end

Hopefully, this will be useful to someone at some point. Not that I have not seen many Ruby enthusiasts here, though.

2 Likes