Skip to content

Commit

Permalink
Use JSON::Coder when available
Browse files Browse the repository at this point in the history
Co-authored-by: Jean Boussier <[email protected]>
  • Loading branch information
etiennebarrie and byroot committed Feb 10, 2025
1 parent 424b9b5 commit a24c228
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ GEM
jmespath (1.6.2)
jsbundling-rails (1.3.1)
railties (>= 6.0.0)
json (2.9.1)
json (2.10.0)
jwt (2.10.1)
base64
kamal (2.4.0)
Expand Down
46 changes: 45 additions & 1 deletion activesupport/lib/active_support/json/encoding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,45 @@ def stringify(jsonified)
end
end

if defined?(::JSON::Coder)
class JSONGemCoderEncoder # :nodoc:
JSON_NATIVE_TYPES = [Hash, Array, Float, String, Symbol, Integer, NilClass, TrueClass, FalseClass].freeze
CODER = ::JSON::Coder.new do |value|
json_value = value.as_json
# Handle objects returning self from as_json
if json_value.equal?(value)
next ::JSON::Fragment.new(value.to_json)
end
# Handle objects not returning JSON-native types from as_json
until JSON_NATIVE_TYPES.include?(json_value.class)
json_value = json_value.as_json
end
json_value
end


def initialize(options = nil)
@options = options ? options.dup.freeze : {}.freeze
end

# Encode the given object into a JSON string
def encode(value)
value = value.as_json(@options) unless @options.empty?

json = CODER.dump(value)

if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
json.gsub!(">", '\u003e')
json.gsub!("<", '\u003c')
json.gsub!("&", '\u0026')
end
json.gsub!("\u2028", '\u2028')
json.gsub!("\u2029", '\u2029')
json
end
end
end

class << self
# If true, use ISO 8601 format for dates and times. Otherwise, fall back
# to the Active Support legacy format.
Expand Down Expand Up @@ -126,7 +165,12 @@ def encode_without_options(value) # :nodoc:

self.use_standard_json_time_format = true
self.escape_html_entities_in_json = true
self.json_encoder = JSONGemEncoder
self.json_encoder =
if defined?(::JSON::Coder)
JSONGemCoderEncoder
else
JSONGemEncoder
end
self.time_precision = 3
end
end
Expand Down
13 changes: 13 additions & 0 deletions activesupport/test/json/encoding_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,16 @@ def with_time_precision(value)
ActiveSupport::JSON::Encoding.time_precision = old_value
end
end

if defined?(::JSON::Coder)
class OldJSONEncodingTest < TestJSONEncoding
setup do
@json_encoder = ActiveSupport::JSON::Encoding.json_encoder
ActiveSupport::JSON::Encoding.json_encoder = ActiveSupport::JSON::Encoding::JSONGemEncoder
end

teardown do
ActiveSupport::JSON::Encoding.json_encoder = @json_encoder
end
end
end

0 comments on commit a24c228

Please sign in to comment.