From 618b46b379cf1787df1b2287f0f3de91cb8b9ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 18 Mar 2025 13:55:37 +0100 Subject: [PATCH] ASN.1 JER: Support decoding already decoded JSON data --- lib/asn1/doc/guides/asn1_getting_started.md | 26 +++++ lib/asn1/src/asn1ct_gen.erl | 123 ++++++++++---------- lib/asn1/test/asn1_test_lib.erl | 14 ++- 3 files changed, 96 insertions(+), 67 deletions(-) diff --git a/lib/asn1/doc/guides/asn1_getting_started.md b/lib/asn1/doc/guides/asn1_getting_started.md index 28a95b794308..079c176c7afe 100644 --- a/lib/asn1/doc/guides/asn1_getting_started.md +++ b/lib/asn1/doc/guides/asn1_getting_started.md @@ -233,6 +233,32 @@ module and the NIF library in `asn1/priv_dir` are needed at runtime. By calling function `info/0` in a generated module, you get information about which compiler options were used. +### Special Decode Functionality for JSON Encoding Rules (JER) + +When using the JSON encoding rules, it is possible to call the +`decode/2` function in the following way with data that has already +been decoded by `json:decode/1`: + +```erlang +SomeModule:decode(Type, {json_decoded, Decoded}). +``` + +Example: + +```erlang +1> asn1ct:compile("People", [jer]). +ok +2> Rockstar = {'Person',"Vince Eclipse",roving,50}. +{'Person',"Vince Eclipse",roving,50} +3> {ok,Bin} = 'People':encode('Person', Rockstar). +{ok,<<"{\"name\":\"Vince Eclipse\",\"location\":2,\"age\":50}">>} +4> 'People':decode('Person', Bin). +{ok,{'Person',"Vince Eclipse",roving,50}} +5> 'People':decode('Person', {json_decoded,json:decode(Bin)}). +{ok,{'Person',"Vince Eclipse",roving,50}} + +``` + ### Errors Errors detected at compile-time are displayed on the screen together with line diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 213e4f5b829d..8926be42ac19 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -803,6 +803,7 @@ pgen_dispatcher(Gen, Types) -> false -> ok end, + %% DECODER ReturnRest = proplists:get_bool(undec_rest, Gen#gen.options), Data = case Gen#gen.erule =:= ber andalso ReturnRest of @@ -811,6 +812,31 @@ pgen_dispatcher(Gen, Types) -> end, emit(["decode(Type, ",Data,") ->",nl]), + pgen_dispatcher_decode(Gen, ReturnRest, NoOkWrapper), + + case Gen#gen.jer of + true -> + emit(["jer_decode(Type, ",Data,") ->",nl]), + pgen_dispatcher_decode(Gen#gen{erule=jer}, + ReturnRest, NoOkWrapper); + false -> + ok + end, + + %% REST of MODULE + gen_decode_partial_incomplete(Gen, NoOkWrapper), + gen_partial_inc_dispatcher(Gen), + + case Gen of + #gen{erule=jer} -> + ok; + _ -> + gen_dispatcher(Types, "encode_disp", "enc_"), + gen_dispatcher(Types, "decode_disp", "dec_") + end. + +pgen_dispatcher_decode(Gen, ReturnRest, NoOkWrapper) -> + CurrMod = lists:concat(["'",get(currmod),"'"]), case NoOkWrapper of false -> emit(["try",nl]); @@ -818,85 +844,54 @@ pgen_dispatcher(Gen, Types) -> end, DecWrap = - case {Gen,ReturnRest} of - {#gen{erule=ber},false} -> - asn1ct_func:need({ber,ber_decode_nif,1}), - "element(1, ber_decode_nif(Data))"; - {#gen{erule=ber},true} -> - asn1ct_func:need({ber,ber_decode_nif,1}), - emit([" {Data,Rest} = ber_decode_nif(Data0),",nl]), - "Data"; - {#gen{erule=jer},false} -> - "json:decode(Data)"; - {#gen{erule=jer},true} -> - exit("JER + return rest not supported"); - {_,_} -> - "Data" - end, + case {Gen,ReturnRest} of + {#gen{erule=ber},false} -> + asn1ct_func:need({ber,ber_decode_nif,1}), + "element(1, ber_decode_nif(Data))"; + {#gen{erule=ber},true} -> + asn1ct_func:need({ber,ber_decode_nif,1}), + emit([" {Data,Rest} = ber_decode_nif(Data0),",nl]), + "Data"; + {#gen{erule=jer},false} -> + ~S""" + case Data of + {json_decoded,Decoded} -> Decoded; + _ -> json:decode(Data) + end + """; + {#gen{erule=jer},true} -> + exit("JER + return rest not supported"); + {_,_} -> + "Data" + end, DecodeDisp = ["decode_disp(Type, ",DecWrap,")"], case {Gen,ReturnRest} of - {#gen{erule=ber},true} -> - emit([" Result = ",DecodeDisp,",",nl]), + {#gen{erule=ber},true} -> + emit([" Result = ",DecodeDisp,",",nl]), result_line(NoOkWrapper, ["Result","Rest"]); - {#gen{erule=ber},false} -> - emit([" Result = ",DecodeDisp,",",nl]), + {#gen{erule=ber},false} -> + emit([" Result = ",DecodeDisp,",",nl]), result_line(NoOkWrapper, ["Result"]); - {#gen{erule=jer},false} -> - emit([" Result = ",{call,jer,decode_jer,[CurrMod,"Type",DecWrap]},",",nl]), + {#gen{erule=JER},false} when JER =:= jer -> + emit([" Result = ",{call,jer,decode_jer,[CurrMod,"Type",DecWrap]},",",nl]), result_line(NoOkWrapper, ["Result"]); - - - {#gen{erule=per},true} -> - emit([" {Result,Rest} = ",DecodeDisp,",",nl]), + {#gen{erule=per},true} -> + emit([" {Result,Rest} = ",DecodeDisp,",",nl]), result_line(NoOkWrapper, ["Result","Rest"]); - {#gen{erule=per},false} -> - emit([" {Result,_Rest} = ",DecodeDisp,",",nl]), + {#gen{erule=per},false} -> + emit([" {Result,_Rest} = ",DecodeDisp,",",nl]), result_line(NoOkWrapper, ["Result"]) end, case NoOkWrapper of - false -> - emit([nl,try_catch(),".",nl,nl]); - true -> - emit([".",nl,nl]) - end, - - case Gen#gen.jer of - true -> - emit(["jer_decode(Type, ",Data,") ->",nl]), - case NoOkWrapper of - false -> emit(["try",nl]); - true -> ok - end, - JerDecWrap = "json:decode(Data)", - emit([" Result = ", - {call,jer, - decode_jer, - [CurrMod,"Type",JerDecWrap]},",",nl]), - result_line(false, ["Result"]), - case NoOkWrapper of - false -> - emit([nl,try_catch(),".",nl,nl]); - true -> - emit([".",nl,nl]) - end; false -> + emit([nl,try_catch()]); + true -> ok end, - - - %% REST of MODULE - gen_decode_partial_incomplete(Gen, NoOkWrapper), - gen_partial_inc_dispatcher(Gen), - case Gen of - #gen{erule=jer} -> - ok; - _ -> - gen_dispatcher(Types, "encode_disp", "enc_"), - gen_dispatcher(Types, "decode_disp", "dec_") - end. + emit([".",nl,nl]). result_line(NoOkWrapper, Items) -> S = [" "|case NoOkWrapper of diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl index 7a271cb1d728..a6d0e963a499 100644 --- a/lib/asn1/test/asn1_test_lib.erl +++ b/lib/asn1/test/asn1_test_lib.erl @@ -197,7 +197,7 @@ roundtrip_enc(Mod, Type, Value, ExpectedValue) -> ExpectedValue = Mod:decode(Type, Encoded) end, map_roundtrip(Mod, Type, Encoded), - test_ber_indefinite(Mod, Type, Encoded, ExpectedValue), + test_special(Mod, Type, Encoded, ExpectedValue), Encoded. map_roundtrip(Mod, Type, Encoded) -> @@ -248,9 +248,10 @@ match_value_tuple(I, T1, T2) when I =< tuple_size(T1) -> match_value_tuple(_, _, _) -> ok. -test_ber_indefinite(Mod, Type, Encoded, ExpectedValue) -> +test_special(Mod, Type, Encoded, ExpectedValue) -> case Mod:encoding_rule() of ber -> + %% Test indefinite decoding for BER. Indefinite = iolist_to_binary(ber_indefinite(Encoded)), case Mod:decode(Type, Indefinite) of {ok,ExpectedValue} -> @@ -258,7 +259,14 @@ test_ber_indefinite(Mod, Type, Encoded, ExpectedValue) -> ExpectedValue -> ok end; - _ -> + jer -> + %% Test already decoded JSON for JER. + JsonDecoded = json:decode(Encoded), + case Mod:decode(Type, {json_decoded,JsonDecoded}) of + {ok,ExpectedValue} -> ok; + ExpectedValue -> ok + end; + _ -> ok end.