I have a strange error in crystal lang: if I make a serializable class with the field _timestamp
as UInt128
, the compilation it causes and error of JSON::PullParser
method:
$ crystal build src/myprogram.cr
Showing last frame. Use --error-trace for full trace.
In /usr/lib/crystal/int.cr:1571:11
1571 | value.to_u128
^------
Error: undefined method 'to_u128' for JSON::PullParser
but when you replace UInt128
to UInt64
the compilation error is gone. Is it my fault oer is it a bug in the library?
My program is this:
require "json"
module Tb2md
VERSION = "0.1.0"
enum Priority
Low # 1
Medium # 2
High # 3
end
class Entry
include JSON::Serializable
property _id : UInt32
property _date : Time
property _timestamp : UInt128
property description : String
property isStarred : Bool
property boards : Array(String)
property _isTask : Bool
property isComplete : Bool
property inProgress : Bool
property priority : Priority
end
class Habitat
include JSON::Serializable
property entries : Hash(String, Entry)
end
content = File.open("storage.json") do |file|
file.gets_to_end
end
h = Habitat.from_json(content)
puts h
end
JSON is historically derived from JavaScript, which treated numbers greater then 53 bits as floating point numbers (it only later added support for BigInt
). I believe that is the reason why the Crystal JSON implementation rightfully rejects UInt128
; otherwise, it risks to be incompatible to other implementations. Even 64 bit numbers are a grey area if more then 53 bits are used. If you need to represent big integers precisely and in a portable way, you have to encode them as strings in JSON.
This is a minimal reproducible example (Uint128
fails to compile, but UInt64
or Float64
will work):
require "json"
class Entry
include JSON::Serializable
property x : UInt128 # <-- will not compile
end
puts (Entry.from_json("{ \"x\": 1 }").to_json)