@@ -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