voxpupuli / json-schema

Ruby JSON Schema Validator
MIT License
1.52k stars 241 forks source link

how to remove in a hash the properties that do not exists in its JSON schema? #443

Open gordielachance opened 4 years ago

gordielachance commented 4 years ago

Hi ! Thanks for your awesome work.

I would like to use your validator to "clean" my input compared to the JSON schema. Thing is, for what I see, JSON::Validator.validate only returns a boolean and does not offer a way to return a "cleaned" hash. Would it be possible ?

schema = {
  'type': 'object',
  'properties': {
    'A'=>{
      'type': 'object',
      'properties': {
        'AsubA'=>{
          'type': 'string',
        },
        'AsubB'=>{
          'type': 'string',
          'default'=>'it s',
          'readOnly'=>true
        },
        'AsubC'=>{
          'type': 'string',
          'default'=>'foo',
          'readOnly'=>true
        },
      }
    },
    'B'=>{
      'type': 'string',
      'default'=>'bar',
    }
  }
}

input = {
  'A'=>{
    'AsubA'=>'hello',
    'AsubC'=>'me',
    'AsubD'=>'i was',
    'AsubE'=>'wondering',
  },
  'B'=>'if after',
  'C'=>'all those years',
}

the magic would happen here and give

output = {
  'A'=>{
    'AsubA'=>'hello',
    'AsubB'=>'it's', #filled with the default value of the schema since input was not set
    'AsubC'=>'foo', #schema is readonly and has a default
  },
  'B'=>'if after',
}

thanks

gordielachance commented 4 years ago

I currently use this function to set the defaults based on the schema, but this does not remove the unecessary properties (which I would need). AND it is redundant since you already do this type of work in your code. You should just make it accessible ...

Thanks

def defaultsFromSchema(node,readonly=false,nodekeys=[],datas={})
  return unless node.is_a?(Hash)

  defaultValue = node.dig('default');
  isReadOnly = ( node.dig('readOnly') === true )

  if defaultValue
    if ( !readonly || (readonly && isReadOnly) )
      #create hash and set deep keys
      return nodekeys.reverse.inject(defaultValue) { |a, n| { n => a } }
    end

  else
    node.each do |k, v|
      keys = nodekeys.clone
      if k != 'properties'
        keys.push(k)
      end

      append = defaultsFromSchema(v,readonly,keys,datas)
      if append
        datas = datas.deep_merge(append)
      end

    end
  end

  datas

end

defaults = defaultsFromSchema(schema) 
readonly = defaultsFromSchema(schema,true)
output = defaults.deep_merge(input).deep_merge(readonly)

With this, I almost got it except that I don't know how to remove the unecessary keys in output.