问题
We are trying to move from SOAP to REST and we stumbled across this issue Here party can be of type Individual or Organization.
Sample XMLs
<customer>
<details>
<party xsi:type="Individual">
<externalID>ABC123</externalID>
<firstname>John</firstname>
<lastname>Smith</lastname>
</party>
</details>
</customer>
<customer>
<details>
<party xsi:type="Organization">
<externalID>APPLE</externalID>
<organizationName>Apple Inc</organizationName>
<listingName>APPLE</listingName>
</party>
</details>
</customer>
However when we move to JSON representation of the same, we encounter the problem where the inheritance information is lost
JSON Sample
{
"customer": {
"details": {
"party": {
"externalID": "ABC123",
"firstname": "John",
"lastname": "Smith"
}
}
}
}
{
"customer": {
"details": {
"party": {
"externalID": "APPLE",
"organizationName": "Apple Inc",
"listingName": "APPLE"
}
}
}
}
So when we convert the JSON back to Java object using libraries like Gson, we loose the definition of Individual or Organization.
While one of the workaround is to build additional services to retrieve the "details" returning the concrete types (Individual or Organization), is there any other approach to handle this in JSON?
回答1:
It's been possible to combine schemas using keywords such as oneOf, allOf, anyOf and get the payload validated since JSON schema v1.0.
https://spacetelescope.github.io/understanding-json-schema/reference/combining.html
However, composition has been enhanced by the keyword discriminator incorporated on OpenAPI (former Swagger) to barely support polymorphism. In OpenAPI 3.0, this support has been enhanced by the addition of oneOf keyword.
Your inheritance could be modeled using a combination of oneOf (for choosing one of the children) and allOf (for combining parent and child).
paths:
/customers:
post:
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/Individual
- $ref: '#/components/schemas/Organization
discriminator:
propertyName: customer_type
responses:
'201':
description: Created
components:
schemas:
Customer:
type: object
required:
- customer_type
- externalID
properties:
customer_type:
type: string
externalID:
type: integer
discriminator:
property_name: customer_type
Individual:
allOf:
- $ref: "#/components/schemas/Customer"
- type: object
- properties:
firtName
type: string
lastName
type: string
Organisation:
allOf:
- $ref: "#/components/schemas/Customer"
- type: object
- properties:
organisationName
type: string
listingName
type: string
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaComposition
18/02/2020 Edit:
For generating Java code using Jackson, OpenAPITools has seemingly been fixed by the PR #5120
回答2:
You can write a custom Gson deserializer and based on existence of organisationName field you create instance of either Organization or Individual class in this deserializar. Something like what is specified here: Gson - deserialization to specific object type based on field value, instead of "type" you can check for the existence of your property.
回答3:
I found a resource submitted by IBM and felt it may be of some help. It's linked below.
On pg. 27 they convert:
<year xsi:type="xs:positiveInteger">1989</year>
To:
{ "xsi:type" : "xs:positiveInteger", value : 1989 }
Thus, I think your code should convert to:
{
"customer": {
"details": {
"party": {
"xsi:type": "Individual",
"externalID": "ABC123",
"firstname": "John",
"lastname": "Smith"
}
}
}
}
{
"customer": {
"details": {
"party": {
"xsi:type": "Organization",
"externalID": "APPLE",
"organizationName": "Apple Inc",
"listingName": "APPLE"
}
}
}
}
Resource for reference: https://www.w3.org/2011/10/integration-workshop/s/ExperienceswithJSONandXMLTransformations.v08.pdf
来源:https://stackoverflow.com/questions/38626716/json-and-object-inheritance