using DataStructures function dictify2(x; keytype::Type=Any, sort_order::Union{Nothing, Vector}=nothing) # Dict-like objects if x isa AbstractDict out = OrderedDict{keytype, Any}() # 1. Process and normalize all keys from the input dictionary processed_dict = OrderedDict{keytype, Any}() for (k, v) in x if keytype === String newk = string(k) elseif keytype === Symbol newk = Symbol(string(k)) else newk = k end processed_dict[newk] = dictify(v; keytype=keytype, sort_order=sort_order) end # 2. If a sort order is specified, apply it if !isnothing(sort_order) # Normalize the sort_order elements to match the requested keytype normalized_order = map(sort_order) do tk if keytype === String return string(tk) elseif keytype === Symbol return Symbol(string(tk)) else return tk end end # First, insert keys that match the requested order for target_key in normalized_order if haskey(processed_dict, target_key) out[target_key] = processed_dict[target_key] end end # Then, append any remaining keys that weren't in the sort_order for (k, v) in processed_dict if !haskey(out, k) out[k] = v end end else # If no sort order is given, just use the processed dict out = processed_dict end return out # Arrays / vectors: map elements recursively elseif x isa AbstractArray return [dictify(element; keytype=keytype, sort_order=sort_order) for element in x] # Everything else: return as-is else return x end end function dict_to_string_html2(d::AbstractDict; indent_level=1, indent_str=" ") lines = String[] padding = indent_str ^ indent_level # Sort keys for predictable, clean output for k in keys(d) v = d[k] if v isa AbstractDict # Open tag, recurse for children, then close tag push!(lines, "$padding<$k>") ind_level = indent_level + 1 push!(lines, dict_to_string_html(v; indent_level=ind_level, indent_str=indent_str)) push!(lines, "$padding") else # Leaf node: put key and value on a single line push!(lines, "$padding<$k>$v") end end return join(lines, "\n") end