santhosh-tekuri / jsonschema

JSONSchema (draft 2020-12, draft 2019-09, draft-7, draft-6, draft-4) Validation using Go
Apache License 2.0
957 stars 98 forks source link

Feature: marshal a default payload based on jsonSchema #95

Closed Icirus closed 1 year ago

Icirus commented 1 year ago

I am looking for a little guidance the Validation is working wonderfully. I am wanting to enhance a payload by setting defaults based on the defaults for a value that are specified in the Json Schema.

I think this could be done similar to the validation process, but the default value would be set in the provided interface. The output of the method could be a []byte for ease of use.

I am unsure what the best method for trying to implement this would be. I have played around with iterating through the properties of the Schema to create a default structure recursively. I am just unsure if there is a better way to achieve the outcome I am after.

santhosh-tekuri commented 1 year ago

default keyword is not clear how it can be used in spec. so such feature cannot be included in library.

but what you are asking can be done trivially as follows:

func Example_FillDefaults() {
    c := jsonschema.NewCompiler()
    c.ExtractAnnotations = true
    err := c.AddResource("schema.json", strings.NewReader(`{
      "type": "object",
      "properties": {
        "foo": {"type": "number"},
        "bar": {"type": "string", "default": "baz"}
      },
      "required": ["foo", "bar"]
    }`))
    if err != nil {
        panic(err)
    }
    sch, err := c.Compile("schema.json")
    if err != nil {
        panic(err)
    }
    data := map[string]interface{}{"foo": 1}
    fillDefaults(sch, data)
    fmt.Println(data["bar"])
    // Output:
    // baz
}

func fillDefaults(sch *jsonschema.Schema, data interface{}) {
    m, ok := data.(map[string]interface{})
    if !ok {
        return
    }
    if sch.Ref != nil {
        fillDefaults(sch.Ref, data)
    }
    for pname, psch := range sch.Properties {
        pval, ok := m[pname]
                If ok {
                        fillDefaults(psch, pval)
                 } else if psch.Default != nil {
            m[pname] = psch.Default
        }
    }
}

in above example property bar is filled with its default value baz

note that the above implementation is not complete. I am just checking Schema.Ref, but complete implementation has to check allOf, oneOff etc