'Validate records (AJAX). This method reads a JSON object from
the request body, validates it against the current resource,
and returns a JSON object with either the validation errors or
the text representations of the data.
@param r: the S3Request
@param attr: dictionary of parameters for the method handler
Input JSON format:
{"<fieldname>":"<value>", "<fieldname>":"<value>"}
Output JSON format:
{"<fieldname>": {"value":"<value>",
"text":"<representation>",
"_error":"<error message>"}}
The input JSON can also be a list of multiple records. This
will return a list of results accordingly. Note that "text"
is not provided if there was a validation error, and vice
versa.
The record ID should always be present in the JSON to
avoid false duplicate errors.
Non-existent fields will return "invalid field" as _error.
Representations will be URL-escaped and any markup stripped.
The ?component=<alias> URL query can be used to specify a
component of the current resource rather than the main table.
This method does only accept .json format.'

def validate(self, r, **attr):

    if (r.representation != 'json'):
        r.error(415, current.ERROR.BAD_FORMAT)
    resource = self.resource
    get_vars = r.get_vars
    if ('component' in get_vars):
        alias = get_vars['component']
    else:
        alias = None
    if ('resource' in get_vars):
        tablename = get_vars['resource']
        if (tablename != ('%s_%s' % (r.controller, r.function))):
            customise = current.deployment_settings.customise_resource(tablename)
            if customise:
                customise(r, tablename)
        components = ([alias] if alias else None)
        try:
            resource = current.s3db.resource(tablename, components=components)
        except:
            r.error(404, current.ERROR.BAD_RESOURCE)
    if alias:
        if (alias in resource.components):
            component = resource.components[alias]
        else:
            r.error(404, current.ERROR.BAD_RESOURCE)
    else:
        component = resource
    source = r.body
    source.seek(0)
    try:
        data = json.load(source)
    except ValueError:
        r.error(501, current.ERROR.BAD_SOURCE)
    if (not isinstance(data, list)):
        single = True
        data = [data]
    else:
        single = False
    table = component.table
    pkey = table._id.name
    get_config = current.s3db.get_config
    tablename = component.tablename
    onvalidation = get_config(tablename, 'onvalidation')
    update_onvalidation = get_config(tablename, 'update_onvalidation', onvalidation)
    create_onvalidation = get_config(tablename, 'create_onvalidation', onvalidation)
    output = []
    for record in data:
        has_errors = False
        if (pkey in record):
            original = {pkey: record[pkey]}
        elif ('_id' in record):
            original = {pkey: record['_id']}
        else:
            original = None
        fields = Storage()
        for fname in record:
            error = None
            validated = fields[fname] = Storage()
            if (fname in (pkey, '_id')):
                continue
            value = record[fname]
            if (fname not in table.fields):
                validated['value'] = value
                validated['_error'] = 'invalid    field'
                continue
            else:
                field = table[fname]
            field = table[fname]
            if (field.type == 'integer'):
                if (value not in (None, '')):
                    try:
                        value = int(value)
                    except ValueError:
                        value = 0
                else:
                    value = None
            if (field.type == 'double'):
                if (value not in (None, '')):
                    try:
                        value = float(value)
                    except ValueError:
                        value = 0
                else:
                    value = None
            widget = field.widget
            if isinstance(widget, S3Selector):
                (value, error) = widget.validate(value, requires=field.requires)
                validated['value'] = (widget.serialize(value) if (not error) else value)
                widget_represent = widget.represent
            else:
                try:
                    (value, error) = s3_validate(table, fname, value, original)
                except AttributeError:
                    error = 'invalid    field'
                validated['value'] = (field.formatter(value) if (not error) else value)
                widget_represent = None
            if error:
                has_errors = True
                validated['_error'] = s3_unicode(error)
            elif widget_represent:
                try:
                    text = widget_represent(value)
                except:
                    text = s3_unicode(value)
                validated['text'] = text
            else:
                try:
                    text = s3_represent_value(field, value=value)
                except:
                    text = s3_unicode(value)
                validated['text'] = text
        if (not has_errors):
            if (original is not None):
                onvalidation = update_onvalidation
            else:
                onvalidation = create_onvalidation
            form = Storage(vars=Storage(record), errors=Storage())
            if (onvalidation is not None):
                callback(onvalidation, form, tablename=tablename)
            for fn in form.errors:
                msg = s3_unicode(form.errors[fn])
                if (fn in fields):
                    validated = fields[fn]
                    has_errors = True
                    validated._error = msg
                    if ('text' in validated):
                        del validated['text']
                else:
                    msg = ('%s:    %s' % (fn, msg))
                    if ('_error' in fields):
                        fields['_error'] = '\n'.join([msg, fields['_error']])
                    else:
                        fields['_error'] = msg
        output.append(fields)
    if (single and (len(output) == 1)):
        output = output[0]
    return json.dumps(output, separators=SEPARATORS)
