MCPcopy Create free account
hub / github.com/PerpetualSoftware/pad / flexJSONToString

Function flexJSONToString

internal/models/item.go:856–907  ·  view source on GitHub ↗

flexJSONToString accepts a raw JSON value and returns it as a canonical JSON-encoded string. Acceptable inbound shapes: - absent / empty / null → nil (caller leaves the field unchanged) - JSON-encoded string whose INNER content is either empty (the legacy empty-string sentinel handled downstream by

(raw json.RawMessage, expectedStart byte, errInvalid error)

Source from the content-addressed store, hash-verified

854// is supposed to add. The pre-existing ItemUpdate fields/tags path
855// inherits the same tightening because it routes through this helper.
856func flexJSONToString(raw json.RawMessage, expectedStart byte, errInvalid error) (*string, error) {
857 trimmed := bytes.TrimSpace(raw)
858 if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) {
859 return nil, nil
860 }
861 switch trimmed[0] {
862 case '"':
863 // JSON-encoded string envelope — unmarshal to the underlying Go
864 // string and then validate the inner content's shape matches
865 // expectedStart. Subsequent code that does
866 // json.Unmarshal([]byte(*s), ...) sees the inner JSON, not a
867 // re-quoted string.
868 var s string
869 if err := json.Unmarshal(trimmed, &s); err != nil {
870 return nil, errInvalid
871 }
872 // Empty inner string is the empty-string sentinel; store-layer
873 // coercion handles it (IDEA-1486). Don't reject here so callers
874 // retain the legacy "" → default normalization shape.
875 innerTrimmed := bytes.TrimSpace([]byte(s))
876 if len(innerTrimmed) == 0 {
877 return &s, nil
878 }
879 if innerTrimmed[0] != expectedStart {
880 return nil, errInvalid
881 }
882 // Confirm the inner content actually parses as JSON of the
883 // expected shape. Catches strings that start with the right
884 // brace but are otherwise garbage (e.g. `"{not valid"`).
885 var inner any
886 if err := json.Unmarshal(innerTrimmed, &inner); err != nil {
887 return nil, errInvalid
888 }
889 return &s, nil
890 case expectedStart:
891 // Object or array — re-marshal to a canonical JSON string so
892 // the downstream string-typed pipeline can json.Unmarshal it
893 // back to a map/slice exactly as if the caller had stringified.
894 var v any
895 if err := json.Unmarshal(trimmed, &v); err != nil {
896 return nil, errInvalid
897 }
898 b, err := json.Marshal(v)
899 if err != nil {
900 return nil, errInvalid
901 }
902 s := string(b)
903 return &s, nil
904 default:
905 return nil, errInvalid
906 }
907}
908
909type ItemListParams struct {
910 CollectionSlug string

Callers 6

UnmarshalJSONMethod · 0.85
UnmarshalJSONMethod · 0.85
UnmarshalJSONMethod · 0.85
UnmarshalJSONMethod · 0.85
UnmarshalJSONMethod · 0.85
UnmarshalJSONMethod · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected