Anyone knows if there's any good online site for validating JSON messages against an OpenAPI 3.0.x document?
I've got the following signature for a path:
/api/equipamentos:
post:
...
put:
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/MsgAtualizacaoComputador'
- $ref: '#/components/schemas/MsgAtualizacaoEquipamentoAtivo'
- $ref: '#/components/schemas/MsgAtualizacaoEquipamentoGenerico'
- $ref: '#/components/schemas/MsgAtualizacaoImpressora'
- $ref: '#/components/schemas/MsgAtualizacaoSoftware'
...
Where the types look like this:
MsgAtualizacaoEquipamento:
required:
- $type
type: object
properties:
$type:
type: string
serialNumber:
type: string
nullable: true
tag:
type: string
nullable: true
modelo:
type: string
nullable: true
username:
type: string
nullable: true
partNumber:
type: string
nullable: true
idFabricante:
type: integer
format: int32
dataAquisicao:
type: string
format: date-time
dataFimGarantia:
type: string
format: date-time
data:
type: string
format: date-time
idFornecedor:
type: integer
format: int32
observacoes:
type: string
nullable: true
estadoEquipamento:
$ref: '#/components/schemas/EstadoEquipamento'
idLocalTrabalho:
type: integer
format: int32
idFuncionario:
type: integer
format: int32
nullable: true
localizacaoArmazem:
type: string
nullable: true
importGuid:
type: string
format: uuid
etiquetaImpressaEm:
type: string
format: date-time
nullable: true
dataRetoma:
type: string
format: date-time
nullable: true
idImportacao:
type: integer
format: int32
dadosGerfip:
$ref: '#/components/schemas/DadosGerfipv2'
id:
type: integer
format: int32
version:
type: integer
format: int32
additionalProperties: false
discriminator:
propertyName: $type
mapping:
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoComputador, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoComputador'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoAtivo, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoEquipamentoAtivo'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoGenerico, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoEquipamentoGenerico'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoImpressora, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoImpressora'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoSoftware, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoSoftware'
MsgAtualizacaoEquipamentoAtivo:
type: object
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
properties:
nome:
type: string
nullable: true
numPortas:
type: integer
format: int32
ip:
type: string
nullable: true
velocidade:
type: string
nullable: true
idTipoEquipamentoAtivo:
type: integer
format: int32
additionalProperties: false
MsgAtualizacaoEquipamentoComEquipamentosAssociados:
required:
- $type
type: object
properties:
$type:
type: string
serialNumber:
type: string
nullable: true
tag:
type: string
nullable: true
modelo:
type: string
nullable: true
username:
type: string
nullable: true
partNumber:
type: string
nullable: true
idFabricante:
type: integer
format: int32
dataAquisicao:
type: string
format: date-time
dataFimGarantia:
type: string
format: date-time
data:
type: string
format: date-time
idFornecedor:
type: integer
format: int32
observacoes:
type: string
nullable: true
estadoEquipamento:
$ref: '#/components/schemas/EstadoEquipamento'
idLocalTrabalho:
type: integer
format: int32
idFuncionario:
type: integer
format: int32
nullable: true
localizacaoArmazem:
type: string
nullable: true
importGuid:
type: string
format: uuid
etiquetaImpressaEm:
type: string
format: date-time
nullable: true
dataRetoma:
type: string
format: date-time
nullable: true
idImportacao:
type: integer
format: int32
dadosGerfip:
$ref: '#/components/schemas/DadosGerfipv2'
id:
type: integer
format: int32
version:
type: integer
format: int32
idsEquipamentosAssociar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
idsEquipamentosCancelar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
idsEquipamentosPropagar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
additionalProperties: false
discriminator:
propertyName: $type
mapping:
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoComputador, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoComputador'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoSoftware, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoSoftware'
MsgAtualizacaoEquipamentoGenerico:
type: object
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
properties:
idTipoEquipamentoGenerico:
type: integer
format: int32
additionalProperties: false
MsgAtualizacaoEquipamentos:
type: object
properties:
observacoes:
type: string
nullable: true
bytes:
type: string
format: byte
nullable: true
additionalProperties: false
MsgAtualizacaoFuncionario:
type: object
properties:
idFuncionario:
type: integer
format: int32
version:
type: integer
format: int32
nif:
type: string
nullable: true
idLocalTrabalho:
type: integer
format: int32
nome:
type: string
nullable: true
carreira:
type: string
nullable: true
numeroMecanografico:
type: integer
format: int32
codUnidadeOrganica:
type: integer
format: int32
situacaoProfissional:
$ref: '#/components/schemas/SituacaoProfissional'
contactos:
type: array
items:
$ref: '#/components/schemas/Contacto'
nullable: true
propagaLocalTrabalhoParaEquipamentos:
type: boolean
additionalProperties: false
MsgAtualizacaoImpressora:
type: object
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
properties:
idTipoImpressora:
type: integer
format: int32
ip:
type: string
nullable: true
nomeNetBIOS:
type: string
nullable: true
additionalProperties: false
MsgAtualizacaoSoftware:
type: object
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamentoComEquipamentosAssociados'
properties:
numLicencasAdquiridas:
type: integer
format: int32
nome:
type: string
nullable: true
versao:
type: string
nullable: true
upgrade:
type: boolean
additionalProperties: false
We simply can't pass any message without getting validation errors. For instance, here's a sample which can't pass the validation test:
{
"$type": "Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoComputador, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"nomeNetBIOS": "XPTO",
"ip": "",
"processador": "IU7-155H",
"memoria": "16GB",
"disco": "512GB",
"gateway": "",
"idTipoComputador": 3,
"idsEquipamentosAssociar": [],
"idsEquipamentosCancelar": [],
"idsEquipamentosPropagar": [],
"id": 83695,
"version": 6,
"serialNumber": "PWOCWVQD",
"tag": "TAG000082196",
"modelo": "Lenovo too",
"username": "",
"partNumber": "21MR004BPG",
"idFabricante": 153,
"dataAquisicao": "2024-12-16T00:00:00+00:00",
"dataFimGarantia": "2027-12-15T00:00:00+00:00",
"data": "0001-01-01T00:00:00",
"idFornecedor": 57,
"observacoes": "None",
"estadoEquipamento": 4,
"idLocalTrabalho": 807,
"idFuncionario": 35112,
"localizacaoArmazem": "",
"importGuid": "00000000-0000-0000-0000-000000000000",
"etiquetaImpressaEm": "2025-01-07T10:27:25.6946923+00:00",
"dataRetoma": null,
"idImportacao": 0,
"dadosGerfip": {
"$type": "Sra.Assistencias.Dtos.Equipamentos.DadosGerfipv2, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"procedimentoAquisitivo": "INTERREG VIU-D MAC 2121-2027",
"numeroInventario": "1000310010",
"dataImportacaoInventario": null
}
}
I've already checked and triple checked the JSON message and I can't see anything wrong with it. Any ideas on what's going on?
Seems you have run into a very common mistake with JSON Schema, particularly older draft versions like the one used in OpenAPI 3.0.x (JSON Schema draft-04)
allOf
and additionalProperties: false
don't really play nice together.
You can see an explanation here https://stackoverflow.com/a/79302728/8564731
Furthermore, your allOf construction is malformed, if I am interpreting your intent correctly. All subschemas with an allOf should be inside the array. You have defined allOf and a sibling properties
keyword, which is valid JSON Schema, but I don't think that's how you were intending to use it.
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
properties:
nome:
type: string
nullable: true
numPortas:
type: integer
format: int32
ip:
type: string
nullable: true
velocidade:
type: string
nullable: true
idTipoEquipamentoAtivo:
type: integer
format: int32
additionalProperties: false
allOf:
- $ref: "#/definitions/schemas/MsgAtualizacaoEquipamento"
- properties: # << this should be part of the array
nome:
type: string
nullable: true
numPortas:
type: integer
format: int32
ip:
type: string
nullable: true
velocidade:
type: string
nullable: true
idTipoEquipamentoAtivo:
type: integer
format: int32
additionalProperties: false
should be removed from your schemas. If you really want to constraint your schemas not to allow additional properties, you need to follow the advice of the post I referenced, where you must redefine every possible keyword at the root of the schema. Then you can apply additionalProperties: false.
The last issue, after you fix those items is the discriminator mapping should be a sibling to the oneOf in your request body.
The example provided doesn't match the available schemas you have given us so I had to modify it to make it work correctly.
There are quite a few missing schemas to make it function 100%, but hopefully that gives you an idea of how to fix it.
openapi: 3.0.4
info:
title: test
version: 1.0.0
servers: []
paths:
/thing:
post:
summary: a request
responses:
'200':
description: OK
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamentoAtivo'
- $ref: '#/components/schemas/MsgAtualizacaoEquipamentoGenerico'
- $ref: '#/components/schemas/MsgAtualizacaoImpressora'
- $ref: '#/components/schemas/MsgAtualizacaoSoftware'
discriminator:
propertyName: $type
mapping:
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoAtivo, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoEquipamentoAtivo'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoGenerico, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoEquipamentoGenerico'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoImpressora, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoImpressora'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoSoftware, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoSoftware'
examples:
test:
value:
{
'$type': 'Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoAtivo, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null',
'nomeNetBIOS': 'XPTO',
'ip': '',
'processador': 'IU7-155H',
'memoria': '16GB',
'disco': '512GB',
'gateway': '',
'idTipoComputador': 3,
'idsEquipamentosAssociar': [],
'idsEquipamentosCancelar': [],
'idsEquipamentosPropagar': [],
'id': 83695,
'version': 6,
'serialNumber': 'PWOCWVQD',
'tag': 'TAG000082196',
'modelo': 'Lenovo too',
'username': '',
'partNumber': '21MR004BPG',
'idFabricante': 153,
'dataAquisicao': '2024-12-16T00:00:00+00:00',
'dataFimGarantia': '2027-12-15T00:00:00+00:00',
'data': '0001-01-01T00:00:00',
'idFornecedor': 57,
'observacoes': 'None',
'estadoEquipamento': 4,
'idLocalTrabalho': 807,
'idFuncionario': 35112,
'localizacaoArmazem': '',
'importGuid': '00000000-0000-0000-0000-000000000000',
'etiquetaImpressaEm': '2025-01-07T10:27:25.6946923+00:00',
'dataRetoma': null,
'idImportacao': 0,
'dadosGerfip':
{
'$type': 'Sra.Assistencias.Dtos.Equipamentos.DadosGerfipv2, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null',
'procedimentoAquisitivo': 'INTERREG VIU-D MAC 2121-2027',
'numeroInventario': '1000310010',
'dataImportacaoInventario': null,
},
}
components:
schemas:
MsgAtualizacaoEquipamento:
required:
- $type
type: object
properties:
$type:
type: string
serialNumber:
type: string
nullable: true
tag:
type: string
nullable: true
modelo:
type: string
nullable: true
username:
type: string
nullable: true
partNumber:
type: string
nullable: true
idFabricante:
type: integer
format: int32
dataAquisicao:
type: string
format: date-time
dataFimGarantia:
type: string
format: date-time
data:
type: string
format: date-time
idFornecedor:
type: integer
format: int32
observacoes:
type: string
nullable: true
estadoEquipamento: {}
idLocalTrabalho:
type: integer
format: int32
idFuncionario:
type: integer
format: int32
nullable: true
localizacaoArmazem:
type: string
nullable: true
importGuid:
type: string
format: uuid
etiquetaImpressaEm:
type: string
format: date-time
nullable: true
dataRetoma:
type: string
format: date-time
nullable: true
idImportacao:
type: integer
format: int32
dadosGerfip: {}
id:
type: integer
format: int32
version:
type: integer
format: int32
MsgAtualizacaoEquipamentoAtivo:
type: object
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- properties:
nome:
type: string
nullable: true
numPortas:
type: integer
format: int32
ip:
type: string
nullable: true
velocidade:
type: string
nullable: true
idTipoEquipamentoAtivo:
type: integer
format: int32
MsgAtualizacaoEquipamentoComEquipamentosAssociados:
required:
- $type
type: object
properties:
$type:
type: string
serialNumber:
type: string
nullable: true
tag:
type: string
nullable: true
modelo:
type: string
nullable: true
username:
type: string
nullable: true
partNumber:
type: string
nullable: true
idFabricante:
type: integer
format: int32
dataAquisicao:
type: string
format: date-time
dataFimGarantia:
type: string
format: date-time
data:
type: string
format: date-time
idFornecedor:
type: integer
format: int32
observacoes:
type: string
nullable: true
estadoEquipamento: {}
idLocalTrabalho:
type: integer
format: int32
idFuncionario:
type: integer
format: int32
nullable: true
localizacaoArmazem:
type: string
nullable: true
importGuid:
type: string
format: uuid
etiquetaImpressaEm:
type: string
format: date-time
nullable: true
dataRetoma:
type: string
format: date-time
nullable: true
idImportacao:
type: integer
format: int32
dadosGerfip: {}
id:
type: integer
format: int32
version:
type: integer
format: int32
idsEquipamentosAssociar:
type: array
items: {}
nullable: true
idsEquipamentosCancelar:
type: array
items: {}
nullable: true
idsEquipamentosPropagar:
type: array
items: {}
nullable: true
discriminator:
propertyName: $type
mapping:
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoComputador, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoComputador'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoSoftware, Sra.Assistencias.Dtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoSoftware'
MsgAtualizacaoEquipamentoGenerico:
type: object
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- properties:
idTipoEquipamentoGenerico:
type: integer
format: int32
MsgAtualizacaoEquipamentos:
type: object
properties:
observacoes:
type: string
nullable: true
bytes:
type: string
format: byte
nullable: true
MsgAtualizacaoFuncionario:
type: object
properties:
idFuncionario:
type: integer
format: int32
version:
type: integer
format: int32
nif:
type: string
nullable: true
idLocalTrabalho:
type: integer
format: int32
nome:
type: string
nullable: true
carreira:
type: string
nullable: true
numeroMecanografico:
type: integer
format: int32
codUnidadeOrganica:
type: integer
format: int32
situacaoProfissional: {}
contactos:
type: array
items: {}
nullable: true
propagaLocalTrabalhoParaEquipamentos:
type: boolean
additionalProperties: false
MsgAtualizacaoImpressora:
type: object
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- properties:
idTipoImpressora:
type: integer
format: int32
ip:
type: string
nullable: true
nomeNetBIOS:
type: string
nullable: true
MsgAtualizacaoSoftware:
type: object
properties:
numLicencasAdquiridas:
type: integer
format: int32
nome:
type: string
nullable: true
versao:
type: string
nullable: true
upgrade:
type: boolean
MsgAtualizacaoComputador: {}
Here's a sample of it working correctly