fast-json-stringify icon indicating copy to clipboard operation
fast-json-stringify copied to clipboard

Matching type order

Open ivan-tymoshenko opened this issue 3 years ago • 10 comments

Prerequisites

  • [X] I have written a descriptive issue title
  • [X] I have searched existing issues to ensure the issue has not already been raised

Issue

There are some places where we need to match the type before serializing the data: oneOf, anyOf, and when the type property is an array. The problem is that matching the types is not always mutually exclusive.

Here is the first problem. If a value isn't marked as nullable we should coerce the null value to the value schema type. integer -> 0 number -> 0 string -> "" boolean -> false

What should we do if there are multiple types?

test('nullable string value', (t) => {
  t.plan(1)

  const schema = {
    type: ['string', 'number']
  }

  const stringify = build(schema)
  const value = stringify(null)

  t.equal(value, '""')
})

test('nullable number value', (t) => {
  t.plan(1)

  const schema = {
    type: ['number', 'string']
  }

  const stringify = build(schema)
  const value = stringify(null)

  t.equal(value, '0')
})

Should we match types in the order in which they are declared?

ivan-tymoshenko avatar Jun 01 '22 10:06 ivan-tymoshenko

  1. loop through the oneOf / anyOf array, and check if type match
  2. If one of the type match, serialize
  3. If all of them do not match, fallback to first one

climba03003 avatar Jun 01 '22 10:06 climba03003

What if few of them match?

ivan-tymoshenko avatar Jun 01 '22 10:06 ivan-tymoshenko

What if few of them match?

it should actually break when the first one match. the order is important in serialization.

climba03003 avatar Jun 01 '22 10:06 climba03003

We need to declare somewhere that the order is important in this case.

ivan-tymoshenko avatar Jun 01 '22 10:06 ivan-tymoshenko

Another unclear case. If I define a typed property it will always coerce the value to the defined type, if I add another type, coercion will not work.

test('integer type', (t) => {
  t.plan(1)

  const schema = {
    type: 'integer'
  }

  const stringify = build(schema)
  const value = stringify('42')
  t.equal(value, '42')
})

test('integer or boolean type', (t) => {
  t.plan(1)

  const schema = {
    type: ['integer', 'boolean']
  }

  const stringify = build(schema)
  const value = stringify('42')
  t.equal(value, '42') // fails
})

ivan-tymoshenko avatar Jun 01 '22 10:06 ivan-tymoshenko

In my point of view, array of type should behave like oneOf.

climba03003 avatar Jun 01 '22 13:06 climba03003

oneOf has the same issue. It doesn't coerce type by default.

ivan-tymoshenko avatar Jun 01 '22 13:06 ivan-tymoshenko

oneOf has the same issue. It doesn't coerce type by default.

Yes, because oneOf really means one of the array. If non of them is match, then the input do not match what this user want.

But, it can be coerce by enable the ajv coerceTypes option. It will always coerce to the first matching type.

climba03003 avatar Jun 01 '22 13:06 climba03003

So this is the correct behavior? https://github.com/fastify/fast-json-stringify/issues/450#issuecomment-1143448071

ivan-tymoshenko avatar Jun 01 '22 13:06 ivan-tymoshenko

So this is the correct behavior? #450 (comment)

I would say yes.

If it need to be coerce by default. It means we need to implement a set of rules, how to determine it can be coerce (it may always match the first type). And do the checking. I think it will be a very heavy job and significant impact the performance.

climba03003 avatar Jun 01 '22 13:06 climba03003