Skip to content

Commit 3eeecbb

Browse files
committed
Refactor rfc8785_sort_keys_proc
1 parent e19f7d7 commit 3eeecbb

1 file changed

Lines changed: 60 additions & 26 deletions

File tree

‎lib/json/ext.rb‎

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,56 @@ def shareable_lambda(block) # :nodoc:
3838
block
3939
end
4040
end
41+
42+
def valid_encoding?(str) # :nodoc:
43+
str.ascii_only? || (str.encoding == Encoding::UTF_8 && str.valid_encoding?)
44+
end
45+
46+
def coerce_key(key, as_json) # :nodoc:
47+
as_json_called = false
48+
49+
unless String === key || Symbol === key
50+
key = as_json.call(key, true)
51+
as_json_called = true
52+
end
53+
54+
if Symbol === key
55+
key = key.name
56+
end
57+
58+
unless String === key
59+
raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key)
60+
end
61+
62+
unless valid_encoding?(key)
63+
if as_json_called
64+
raise GeneratorError.new("source sequence is illegal/malformed utf-8", key)
65+
end
66+
67+
key = as_json.call(key, true)
68+
unless String === key
69+
raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key)
70+
end
71+
72+
unless Ext.valid_encoding?(key)
73+
raise GeneratorError.new("source sequence is illegal/malformed utf-8", key)
74+
end
75+
end
76+
77+
key
78+
end
79+
80+
def on_duplicate_key(hash, as_json)
81+
set = {}
82+
hash.each_key do |key|
83+
key = coerce_key(key, as_json)
84+
if set[key]
85+
raise GeneratorError.new("detected duplicate key #{key.inspect}", key)
86+
else
87+
set[key] = true
88+
end
89+
end
90+
end
4191
end
4292

4393
require 'json/ext/parser'
@@ -101,33 +151,17 @@ def shareable_lambda(block) # :nodoc:
101151
})
102152

103153
generator::State.rfc8785_sort_keys_proc = shareable_lambda(->(hash, as_json) {
104-
if as_json
105-
converted = {}
106-
hash.each do |key, value|
107-
string = (String === key || Symbol === key) ? key.to_s : nil
108-
unless string && (string.ascii_only? || (string.encoding == Encoding::UTF_8 && string.valid_encoding?))
109-
key = as_json.call(key, true)
110-
unless String === key || Symbol === key
111-
raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key)
112-
end
113-
begin
114-
string = key.to_s.encode(Encoding::UTF_8)
115-
rescue EncodingError => error
116-
raise GeneratorError.new(error.message, key)
117-
end
118-
unless string.valid_encoding?
119-
raise GeneratorError.new("source sequence is illegal/malformed utf-8", key)
120-
end
121-
key = string
122-
end
123-
if converted.key?(key)
124-
raise GeneratorError.new("detected duplicate key #{key.inspect}", key)
125-
end
126-
converted[key] = value
127-
end
128-
hash = converted
154+
converted = if as_json
155+
hash.transform_keys { |k| Ext.coerce_key(k, as_json) }
156+
else
157+
hash.transform_keys(&:to_s)
129158
end
130-
hash.sort_by { |k,| k.to_s.encode(Encoding::UTF_16) }.to_h
159+
160+
if converted.size < hash.size
161+
Ext.on_duplicate_key(hash, as_json)
162+
end
163+
164+
converted.sort_by { |k,| k.encode(Encoding::UTF_16) }.to_h
131165
})
132166

133167
JSON.generator = generator

0 commit comments

Comments
 (0)