···201201 else
202202 (* Strings accept quoted scalars or non-null plain scalars *)
203203 map.dec meta value
204204+ | Array map ->
205205+ (* Treat null as an empty array for convenience *)
206206+ if is_null_scalar value then
207207+ let end_meta = meta_of_span d ev.Event.span in
208208+ map.dec_finish end_meta 0 (map.dec_empty ())
209209+ else
210210+ err_type_mismatch d ev.span t ~fnd:"scalar"
211211+ | Object map ->
212212+ (* Treat null as an empty object for convenience *)
213213+ if is_null_scalar value then
214214+ (* Build a dict with all default values from absent members *)
215215+ let add_default _ (Mem_dec mem_map) dict =
216216+ match mem_map.dec_absent with
217217+ | Some v -> Dict.add mem_map.id v dict
218218+ | None ->
219219+ (* Required field without default - error *)
220220+ let exp = String_map.singleton mem_map.name (Mem_dec mem_map) in
221221+ missing_mems_error meta map ~exp ~fnd:[]
222222+ in
223223+ let dict = String_map.fold add_default map.mem_decs Dict.empty in
224224+ let dict = Dict.add object_meta_arg meta dict in
225225+ apply_dict map.dec dict
226226+ else
227227+ err_type_mismatch d ev.span t ~fnd:"scalar"
204228 | Map m ->
205229 (* Handle Map combinators (e.g., from Jsont.option) *)
206230 m.dec (decode_scalar_as d ev value style m.dom)
+52-2
lib/yamlt.mli
···2929 let from_yaml = Yamlt.decode_string Config.jsont yaml_str
3030 ]}
31313232- See notes about {{!yaml_mapping}YAML to JSON mapping} and
3333- {{!yaml_scalars}YAML scalar resolution}. *)
3232+ See notes about {{!yaml_mapping}YAML to JSON mapping},
3333+ {{!yaml_scalars}YAML scalar resolution}, and
3434+ {{!null_handling}null value handling}. *)
34353536open Bytesrw
3637···198199 When decoding against a specific {!Jsont.t} type, the expected type takes
199200 precedence over automatic resolution. For example, decoding ["yes"] against
200201 {!Jsont.string} yields the string ["yes"], not [true]. *)
202202+203203+(** {1:null_handling Null Value Handling}
204204+205205+ YAML null values are handled according to the expected type to provide
206206+ friendly defaults while maintaining type safety:
207207+208208+ {b Collections (Arrays and Objects):}
209209+210210+ Null values decode as empty collections when the codec expects a collection
211211+ type. This provides convenient defaults for optional collection fields in
212212+ YAML:
213213+ {[
214214+ # YAML with null collection fields
215215+ config:
216216+ items: null # Decodes as []
217217+ settings: ~ # Decodes as {}
218218+ tags: # Missing value = null, decodes as []
219219+ ]}
220220+221221+ For arrays, null decodes to an empty array. For objects, null decodes to an
222222+ object with all fields set to their [dec_absent] defaults. If any required
223223+ field lacks a default, decoding fails with a missing member error.
224224+225225+ This behavior makes yamlt more forgiving for schemas with many optional
226226+ collection fields, where writing [field:] (which parses as null) is natural
227227+ and semantically equivalent to [field: []].
228228+229229+ {b Numbers:}
230230+231231+ Null values decode to [Float.nan] when the codec expects a number.
232232+233233+ {b Primitive Types (Int, Bool, String):}
234234+235235+ Null values {e fail} when decoding into primitive scalar types ([int],
236236+ [bool], [string]). Null typically indicates genuinely missing or incorrect
237237+ data for these types, and silent conversion could clash with a manual
238238+ setting of the default value (e.g. 0 and [null] for an integer would be
239239+ indistinguishable).
240240+241241+ To accept null for primitive fields, explicitly use {!Jsont.option}:
242242+ {[
243243+ (* Accepts null, decodes as None *)
244244+ Jsont.Object.mem "count" (Jsont.option Jsont.int) ~dec_absent:None
245245+246246+ (* Rejects null, requires a number *)
247247+ Jsont.Object.mem "count" Jsont.int ~dec_absent:0
248248+ ]}
249249+250250+*)