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 # DateTime
puts parsed['uri'].class       # URI::HTTPS

Install

gem install fxf

Author

Mike O'Sullivan [email protected]

History

version date notes
1.0 Jan 27, 2020 Initial upload.