Skip to content

Commit

Permalink
Merge pull request #732 from etiennebarrie/fragment
Browse files Browse the repository at this point in the history
Introduce JSON::Fragment
  • Loading branch information
byroot authored Jan 20, 2025
2 parents f8cfa26 + 9e3500f commit c31ba89
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 1 deletion.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ You can also use the `pretty_generate` method (which formats the output more
verbosely and nicely) or `fast_generate` (which doesn't do any of the security
checks generate performs, e. g. nesting deepness checks).

## Combining JSON fragments

To combine JSON fragments to build a bigger JSON document, you can use `JSON::Fragment`:

```ruby
posts_json = cache.fetch_multi(post_ids) do |post_id|
JSON.generate(Post.find(post_id))
end
posts_json.map { |post_json| JSON::Fragment.new(post_json) }
JSON.generate({ posts: posts_json, count: posts_json.count })
```

## Handling arbitrary types

> [!CAUTION]
Expand Down
18 changes: 17 additions & 1 deletion ext/json/ext/generator/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ typedef struct JSON_Generator_StateStruct {
#define RB_UNLIKELY(cond) (cond)
#endif

static VALUE mJSON, cState, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
static VALUE mJSON, cState, cFragment, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;

static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend, i_encode;
static ID sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
Expand Down Expand Up @@ -68,6 +68,7 @@ static void generate_json_integer(FBuffer *buffer, struct generate_json_data *da
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj);

static int usascii_encindex, utf8_encindex, binary_encindex;

Expand Down Expand Up @@ -971,6 +972,13 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
fbuffer_append_str(buffer, tmp);
}

static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
{
VALUE fragment = RSTRUCT_GET(obj, 0);
Check_Type(fragment, T_STRING);
fbuffer_append_str(buffer, fragment);
}

static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
{
VALUE tmp;
Expand Down Expand Up @@ -1010,6 +1018,10 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
if (klass != rb_cFloat) goto general;
generate_json_float(buffer, data, state, obj);
break;
case T_STRUCT:
if (klass != cFragment) goto general;
generate_json_fragment(buffer, data, state, obj);
break;
default:
general:
if (state->strict) {
Expand Down Expand Up @@ -1546,6 +1558,10 @@ void Init_generator(void)
rb_require("json/common");

mJSON = rb_define_module("JSON");

rb_global_variable(&cFragment);
cFragment = rb_const_get(mJSON, rb_intern("Fragment"));

VALUE mExt = rb_define_module_under(mJSON, "Ext");
VALUE mGenerator = rb_define_module_under(mExt, "Generator");

Expand Down
6 changes: 6 additions & 0 deletions lib/json/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ def detailed_message(...)
# system. Usually this means that the iconv library is not installed.
class MissingUnicodeSupport < JSONError; end

Fragment = Struct.new(:json) do
def to_json(state = nil)
json
end
end

module_function

# :call-seq:
Expand Down
5 changes: 5 additions & 0 deletions test/json/json_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -661,4 +661,9 @@ def test_string_ext_included_calls_super
def test_nonutf8_encoding
assert_equal("\"5\u{b0}\"", "5\xb0".dup.force_encoding(Encoding::ISO_8859_1).to_json)
end

def test_fragment
fragment = JSON::Fragment.new(" 42")
assert_equal '{"number": 42}', JSON.generate({ number: fragment })
end
end

0 comments on commit c31ba89

Please sign in to comment.