FXF
FXF is a JSON structure for serializing interconnected data. It also allows for
serialization of objects of arbitrary classes.
Native JSON only allows for hierarchical data. For example, the following
structure serializes to JSON quite nicely.
hsh = {}
hsh['mary'] = {'name'=>'Mary'}
hsh['fred'] = {'name'=>'Fred'}
That produces JSON like this:
{
"mary": {
"name": "Mary"
},
"fred": {
"name": "Fred"
}
}
However, if you add interconnections to the data you start getting repeated data
in the JSON. For example, consider this structure in which the hash for Mary
includes a reference to the hash for Fred.
hsh = {}
hsh['mary'] = {'name'=>'Mary'}
hsh['fred'] = {'name'=>'Fred'}
hsh['mary']['friend'] = hsh['fred']
In that case you get this structure with redundant information:
{
"mary": {
"name": "Mary",
"friend": {
"name": "Fred"
}
},
"fred": {
"name": "Fred"
}
}
The situation gets worse if data references itself. For example, if the JSON
module tries to implement this structure, an error results.
hsh = {}
hsh['mary'] = {'name'=>'Mary'}
hsh['fred'] = {'name'=>'Fred'}
hsh['mary']['self'] = hsh['mary']
That gives us this nesting error:
Traceback (most recent call last):
2: from ./json.rb:39:in `<main>'
1: from /usr/lib/ruby/2.5.0/json/common.rb:286:in `pretty_generate'
/usr/lib/ruby/2.5.0/json/common.rb:286:in `generate': nesting of 100 is too deep (JSON::NestingError)
FXF preserves the original structure of the object without any difficulties with
redundant or self-nested objects. To generate an FXF string, simply call
FXF.generate. In these examples we add 'pretty'=>true
for readability.
fxf = FXF.generate(hsh, 'pretty'=>true)
So the structure from the previous example would be serialized with this
(admittedly not very human readable) JSON structure:
{
"root": "47283390982220",
"objects": {
"47283390982220": {
"mary": "47283390982160",
"fred": "47283390982120"
},
"47283390982160": {
"name": "47283390982180",
"self": "47283390982160"
},
"47283390982180": "Mary",
"47283390982120": {
"name": "47283390982140"
},
"47283390982140": "Fred"
}
}
To parse that string back into a structure, use FXF.parse
.
parsed = FXF.parse(fxf)
The resulting parsed data has the same structure as the original.
puts parsed['mary'] == parsed['mary']['self'] # true
Custom classes
FXF can serialize data so that the class of the object is preserved. Classes
must be defined in such a way that their objects can be exported to FXF format,
then imported again from that format. Doing so requires implementing the
instance method to_fxf
and the class method from_fxf
. Consider this class.
class MyClass
attr_reader :pk
def initialize(pk)
@pk = pk
end
def to_fxf
return {'pk'=>@pk}
end
def self.from_fxf(details)
return self.new(details['pk'])
end
end
The to_fxf
method returns a hash with information necessary to recreate the
object. In this case just the pk
property is required.
When the object is deserialized, that information is passed to the class'
from_fxf
method. Note that that is a method of the class itself, so it needs
to be defined with self.from_fxf
. The method is given a hash of the same
information that was exported from to_fxf
. In this case, from_fxf
uses that
hash to create a new MyClass
object using the primary key.
Standard classes
FXF can export two standard classes without the need for any additional
modifications to them: DateTime and URI::HTTPS. More common classes will be
added as FXF is developed.
In this example, we create a DateTime
object and a URI::HTTPS
object.
hsh = {}
hsh['timestamp'] = DateTime.parse('Jan 5, 2017, 7:18 am')
hsh['uri'] = URI('https://www.example.com')
fxf = FXF.generate(hsh, 'pretty'=>true)
Identical objects are created when the FXF string is parsed.
parsed = FXF.parse(fxf)
puts parsed['timestamp'].class
puts parsed['uri'].class
Install
gem install fxf
Author
Mike O'Sullivan
mike@idocs.com
History
version | date | notes |
---|
1.0 | Jan 27, 2020 | Initial upload. |