21200: copied branch from arvados-workbench2 Arvados-DCO-1.1-Signed-off-by: Lisa...
[arvados.git] / services / api / lib / serializers.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'safe_json'
6
7 class Serializer
8   class TypeMismatch < ArgumentError
9   end
10
11   def self.dump(val)
12     if !val.is_a?(object_class)
13       raise TypeMismatch.new("cannot serialize #{val.class} as #{object_class}")
14     end
15     SafeJSON.dump(val)
16   end
17
18   def self.legacy_load(s)
19     val = Psych.safe_load(s, permitted_classes: [Time])
20     if val.is_a? String
21       # If apiserver was downgraded to a YAML-only version after
22       # storing JSON in the database, the old code would have loaded
23       # the JSON document as a plain string, and then YAML-encoded
24       # it when saving it back to the database. It's too late now to
25       # make the old code behave better, but at least we can
26       # gracefully handle the mess it leaves in the database by
27       # double-decoding on the way out.
28       return SafeJSON.load(val)
29     else
30       return val
31     end
32   end
33
34   def self.load(s)
35     if s.is_a?(object_class)
36       # Rails already deserialized for us
37       s
38     elsif s.nil?
39       object_class.new()
40     elsif s[0] == first_json_char
41       SafeJSON.load(s)
42     elsif s[0..2] == "---"
43       legacy_load(s)
44     else
45       raise "invalid serialized data #{s[0..5].inspect}"
46     end
47   end
48 end
49
50 class HashSerializer < Serializer
51   def self.first_json_char
52     "{"
53   end
54
55   def self.object_class
56     ::Hash
57   end
58 end
59
60 class ArraySerializer < Serializer
61   def self.first_json_char
62     "["
63   end
64
65   def self.object_class
66     ::Array
67   end
68 end