-
Notifications
You must be signed in to change notification settings - Fork 334
Closed
Labels
Description
The issue I'm seeing can be observed via this test class (written in kotlin):
package networknt.exampletest
import com.fasterxml.jackson.databind.ObjectMapper
import com.networknt.schema.JsonSchema
import com.networknt.schema.JsonSchemaFactory
import com.networknt.schema.SpecVersionDetector
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
class JsonSchemaValidatorTest {
val objectMapper = ObjectMapper()
val validDocWithT2Type = objectMapper.readTree("""
{
"id": "abc",
"details": {
"__typename": "T2",
"name": "Bob"
}
}
""")
val validDocWithT3Type = objectMapper.readTree("""
{
"id": "def",
"details": {
"__typename": "T3",
"description": "Something"
}
}
""")
val t2TypeSchema = """
{
"type": "object",
"properties": {
"name": {"type": "string"},
"__typename": {"const": "T2"}
},
"required": ["name", "__typename"]
}
"""
val t3TypeSchema = """
{
"type": "object",
"properties": {
"description": {"type": "string"},
"__typename": {"const": "T3"}
},
"required": ["description", "__typename"]
}
"""
@Test fun oneOfWithT2AndT3_T2DocIsValid() {
val schema = buildSchema("oneOf", t2TypeSchema, t3TypeSchema)
assertThat(schema.validate(validDocWithT2Type)).isEmpty()
}
@Test fun oneOfWithT2AndT3_T3DocIsValid() {
val schema = buildSchema("oneOf", t2TypeSchema, t3TypeSchema)
assertThat(schema.validate(validDocWithT3Type)).isEmpty()
}
@Test fun oneOfWithT3AndT2_T2DocIsValid() {
val schema = buildSchema("oneOf", t3TypeSchema, t2TypeSchema)
assertThat(schema.validate(validDocWithT2Type)).isEmpty()
}
@Test fun oneOfWithT3AndT3_T3DocIsValid() {
val schema = buildSchema("oneOf", t3TypeSchema, t2TypeSchema)
assertThat(schema.validate(validDocWithT3Type)).isEmpty()
}
@Test fun anyOfWithT2AndT3_T2DocIsValid() {
val schema = buildSchema("anyOf", t2TypeSchema, t3TypeSchema)
assertThat(schema.validate(validDocWithT2Type)).isEmpty()
}
@Test fun anyOfWithT2AndT3_T3DocIsValid() {
val schema = buildSchema("anyOf", t2TypeSchema, t3TypeSchema)
assertThat(schema.validate(validDocWithT3Type)).isEmpty()
}
@Test fun anyOfWithT3AndT2_T2DocIsValid() {
val schema = buildSchema("anyOf", t3TypeSchema, t2TypeSchema)
assertThat(schema.validate(validDocWithT2Type)).isEmpty()
}
@Test fun anyOfWithT3AndT3_T3DocIsValid() {
val schema = buildSchema("anyOf", t3TypeSchema, t2TypeSchema)
assertThat(schema.validate(validDocWithT3Type)).isEmpty()
}
private fun buildSchema(keyword: String, subschema1: String, subschema2: String): JsonSchema {
val schemaString = """
{
"${'$'}schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"T1": {
"type": "object",
"properties": {
"id": "string",
"details": {
"$keyword": [$subschema1, $subschema2]
}
}
}
}
}
"""
val rootNode = objectMapper.readTree(schemaString)
val definitionsNode = rootNode.get("definitions")!!
val specVersion = SpecVersionDetector.detect(rootNode)
val schemaFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(specVersion))
.objectMapper(objectMapper)
.build()
return schemaFactory.getSchema(definitionsNode.get("T1"))
}
}When I run this, all but 2 tests pass. Here are the failures:
oneOfWithT2AndT3_T3DocIsValid:
Expecting empty but was: [$.details.name: is missing but it is required]
oneOfWithT3AndT2_T2DocIsValid:
Expecting empty but was: [$.details.description: is missing but it is required]
As you can see, when the schema uses anyOf, then documents matching either of the 2 subschemas are valid. But when the schema uses oneOf, the validator only considers a document that matches the first subschema to be valid. When we change the order of the subschemas, it changes which of the two example documents are considered valid.
My understanding of oneOf is that a document should be considered valid so long as it matches exactly one of the subschemas (and it doesn't matter which one).