Skip to content

Commit 24c3ba4

Browse files
authored
Merge pull request #127 from zmstone/master
Fix bytes and fixed JSON value decode
2 parents cac7ffe + be31a70 commit 24c3ba4

File tree

4 files changed

+36
-18
lines changed

4 files changed

+36
-18
lines changed

src/avro_json_decoder.erl

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
%%%-----------------------------------------------------------------------------
2-
%%% Copyright (c) 2013-2018 Klarna AB
2+
%%% Copyright (c) 2013-2024 Klarna AB
33
%%%
44
%%% This file is provided to you under the Apache License,
55
%%% Version 2.0 (the "License"); you may not use this file
@@ -371,16 +371,18 @@ parse_prim(V, Type) when ?IS_STRING_TYPE(Type) andalso
371371
is_binary(V) ->
372372
avro_primitive:string(V).
373373

374+
%% Avro bytes and fixed type values are encoded as \u escaped string
375+
%% e.g. \u00ff for 255.
376+
%% The JSON library (jsone) however, tries to decode it as utf8 strings
377+
%% here we try to revert it.
374378
-spec parse_bytes(binary()) -> binary().
375-
parse_bytes(BytesStr) ->
376-
list_to_binary(parse_bytes(BytesStr, [])).
377-
378-
-spec parse_bytes(binary(), [byte()]) -> [byte()].
379-
parse_bytes(<<>>, Acc) ->
380-
lists:reverse(Acc);
381-
parse_bytes(<<"\\u00", B1, B0, Rest/binary>>, Acc) ->
382-
Byte = erlang:list_to_integer([B1, B0], 16),
383-
parse_bytes(Rest, [Byte | Acc]).
379+
parse_bytes(Bytes) ->
380+
Original = unicode:characters_to_list(Bytes, utf8),
381+
try
382+
iolist_to_binary(Original)
383+
catch _:_ ->
384+
error({invalid_bytes_value, Bytes})
385+
end.
384386

385387
-spec parse_record(json_value(), record_type(),
386388
lkup_fun(), decoder_options()) ->

src/avro_json_encoder.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
%% coding: latin-1
22
%%%-----------------------------------------------------------------------------
3-
%%% Copyright (c) 2013-2018 Klarna AB
3+
%%% Copyright (c) 2013-2024 Klarna AB
44
%%%
55
%%% This file is provided to you under the Apache License,
66
%%% Version 2.0 (the "License"); you may not use this file

test/avro_json_decoder_tests.erl

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
%% coding: latin-1
22
%%%-------------------------------------------------------------------
3-
%%% Copyright (c) 2013-2018 Klarna AB
3+
%%% Copyright (c) 2013-2024 Klarna AB
44
%%%
55
%%% This file is provided to you under the Apache License,
66
%%% Version 2.0 (the "License"); you may not use this file
@@ -235,10 +235,24 @@ parse_fixed_type_test() ->
235235
?assertEqual(ExpectedType, Fixed).
236236

237237
parse_bytes_value_test() ->
238-
Json = <<"\\u0010\\u0000\\u00FF">>,
239-
Value = parse_value(Json, avro_primitive:bytes_type(), none),
238+
RawJson = <<"{\"a\":\"\\u0010\\u0000\\u00FF\"}">>,
239+
#{<<"a">> := Bytes} = jsone:decode(RawJson),
240+
?assertEqual([16,0,255], unicode:characters_to_list(Bytes, utf8)),
241+
Value = parse_value(Bytes, avro_primitive:bytes_type(), none),
240242
?assertEqual(avro_primitive:bytes(<<16,0,255>>), Value).
241243

244+
bytes_value_encode_decode_test() ->
245+
Fields = [avro_record:define_field("a", bytes)],
246+
Schema = avro_record:type("Test", Fields, [{namespace, "name.space"}]),
247+
Bytes = iolist_to_binary(lists:seq(0, 255)),
248+
Record = avro_record:new(Schema, [{"a", Bytes}]),
249+
Json = avro_json_encoder:encode_value(Record),
250+
Lkup = fun(_) -> Schema end,
251+
Opts = avro:make_decoder_options([{is_wrapped, false}]),
252+
Decoded = avro_json_decoder:decode_value(Json, Schema, Lkup, Opts),
253+
?assertEqual([{<<"a">>, Bytes}], Decoded),
254+
ok.
255+
242256
parse_record_value_test() ->
243257
%% This test also tests parsing other types inside the record
244258
TestRecord = get_test_record(),
@@ -337,11 +351,13 @@ parse_map_value_test() ->
337351

338352
parse_fixed_value_test() ->
339353
Type = avro_fixed:type("FooBar", 2),
340-
Json = <<"\\u0001\\u007f">>,
354+
RawJson = <<"{\"a\":\"\\u0001\\u007f\"}">>,
355+
#{<<"a">> := Bytes} = jsone:decode(RawJson),
356+
?assertEqual([1,127], unicode:characters_to_list(Bytes, utf8)),
341357
ExpectedValue = avro_fixed:new(Type, <<1,127>>),
342-
?assertEqual(ExpectedValue, parse_value(Json, Type, none)),
358+
?assertEqual(ExpectedValue, parse_value(Bytes, Type, none)),
343359
?assertEqual(<<1,127>>,
344-
parse(Json, Type, none,
360+
parse(Bytes, Type, none,
345361
avro:make_decoder_options([{is_wrapped, false}]))).
346362

347363
parse_value_with_lkup_fun_test() ->

test/data/interop.avsc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
{"name": "boolField", "type": "boolean"},
77
{"name": "floatField", "type": "float"},
88
{"name": "doubleField", "type": "double"},
9-
{"name": "bytesField", "type": "bytes"},
9+
{"name": "bytesField", "type": "bytes", "default": "\u0000"},
1010
{"name": "nullField", "type": "null"},
1111
{"name": "arrayField", "type": {"type": "array", "items": "double"}},
1212
{"name": "mapField", "type":

0 commit comments

Comments
 (0)