{%- if native_return_binding %}
{{ indent }}// Native-object return: the binding struct's `TryConvert` accepts the host's native wrapped
{{ indent }}// object (and still a Hash/JSON via `to_json`); `From<{{ native_return_binding }}>` yields core.
{% if has_error %}
{{ indent }}<{{ native_return_binding }} as magnus::TryConvert>::try_convert(val)
{{ indent }}    .map(Into::into)
{{ indent }}    .map_err(|e| {{ err_convert }})
{% else %}
{{ indent }}<{{ native_return_binding }} as magnus::TryConvert>::try_convert(val).map(Into::into).unwrap_or_default()
{%- endif %}
{%- elif needs_json %}
{{ indent }}let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
{{ indent }}    s
{{ indent }}} else {
{{ indent }}    match val.funcall::<_, _, String>("to_json", ()) {
{{ indent }}        Ok(s) => s,
{{ indent }}        Err(e) => {
{%- if has_error %}
{{ indent }}            return Err({{ err_non_json }});
{%- else %}
{{ indent }}            let _ = e;
{{ indent }}            return Default::default();
{%- endif %}
{{ indent }}        }
{{ indent }}    }
{{ indent }}};
{%- if has_error %}
{{ indent }}serde_json::from_str::<{{ rust_ty }}>(&json_str)
{{ indent }}    .map_err(|e| {{ err_deserialize }})
{%- else %}
{{ indent }}serde_json::from_str::<{{ rust_ty }}>(&json_str).unwrap_or_default()
{%- endif %}
{%- else %}
{%- if has_error %}
{{ indent }}<{{ rust_ty }} as magnus::TryConvert>::try_convert(val)
{{ indent }}    .map_err(|e| {{ err_convert }})
{%- else %}
{{ indent }}<{{ rust_ty }} as magnus::TryConvert>::try_convert(val).unwrap_or_default()
{%- endif %}
{%- endif %}
