Serializing collections
In addition to making our citable collection comparable on URN logic and iterable, we must make it serializable to and from CEX format. When we defined our Isbn10Urn
type, it automatically inherited the UrnComparable
trait because it was a subtype of Urn
. We saw this when we tested a collection with the urncomparable
function.
urncomparable(jane)
true
In contrast, CEX-serializable content does not all fall within a single type hierarchy. Instead, we will implement the CexTrait
from CitableBase
.
Defining our type as serializable
We first define our ReadingList
type as serializable by importing the CexTrait
type and assigning it a value of CexSerializable()
for our type. We can test whether this assignment is recognized using the cexserializable
function from CitableBase
.
import CitableBase: CexTrait
CexTrait(::Type{ReadingList}) = CexSerializable()
cexserializable(rl)
true
When cexserializable
is true, we know that CitableBase
will dispatch functions to our type correctly.
Implementing the required functions
Now we can implement the pair of inverse functions cex
and fromcex
from CitableBase
.
To serialize our collection to CEX format, we'll compose a citecollection
type of CEX block, and simply list each ISBN's string value, one per line.
import CitableBase: cex
function cex(reading::ReadingList; delimiter = "|")
header = "#!citecollection\n"
strings = map(ref -> ref.isbn, reading.reff)
header * join(strings, "\n")
end
cex (generic function with 4 methods)
Let's see what our reading list looks in this format.
cexoutput = cex(rl)
println(cexoutput)
#!citecollection
urn:isbn:022661283X
urn:isbn:022656875X
urn:isbn:022656875X
urn:isbn:1108922036
urn:isbn:0141395203
Now we'll write a function to instantiate a ReadingList
from a string source.
To keep this illustration brief and focused on the design of citable collections, we will naively begin reading data once we see a line containing the block header citecollection
, and just read to the end of the file. This would fail on anything but the most trivial CEX source. For a real application, we would instead use the CiteEXchange
package to work with CEX source data. It includes functions to extract blocks and data lines by type or identifying URN, for example.
import CitableBase: fromcex
function fromcex(src::AbstractString, ReadingList; delimiter = "|")
isbns = []
lines = split(src, "\n")
inblock = false
for ln in lines
if ln == "#!citecollection"
inblock = true
elseif inblock
push!(isbns,Isbn10Urn(ln))
end
end
ReadingList(isbns)
end
fromcex (generic function with 2 methods)
The acid test: can we roundtrip the CEX output back to an equivalent ReadingList
?
rl2 = fromcex(cexoutput, ReadingList)
rl == rl2
true