Skip to content

Java support for discriminator attribute when reading objects #1253

@cknowles

Description

@cknowles

Currently there doesn't seem to be any support for the swagger v2 discriminator attribute when reading an entity. I think the expectation would be that we first read in the discriminator field, then read the rest of the object based on this field. Not sure where best to put this but have been playing around with some custom JsonDeserializers.

Representing the Test Steps from https://www.runscope.com/docs/api/steps, I have Step/RequestStep/ConditionStep/PauseStep. I found two options so far which are below. Both seem roughly equivalent in terms of complexity and code size.

Option 1

The addition of a custom deserializer:

public class StepDeserializer extends JsonDeserializer<Step> {

    public StepDeserializer() {
        super();
    }

    @Override
    public Step deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        String stepType = node.get("step_type").asText();
        StepTypeEnum stepTypeEnum = StepTypeEnum.valueOf(stepType);

        Class<? extends Step> valueType = null;

        switch (stepTypeEnum) {
            case condition:
                valueType = ConditionStep.class;
                break;
            case pause:
                valueType = PauseStep.class;
                break;
            case request:
                valueType = RequestStep.class;
                break;
        }

        if (valueType != null) {
            return mapper.treeToValue(node, valueType);
        } else {
            throw new JsonParseException("Enumeration value of " + stepType + " not recognised",
                    jp.getCurrentLocation());
        }
    }

}

Then this JsonDeserializer can be added to the generation in two ways I think.

Either to the static initialization in JsonUtil:

SimpleModule module = new SimpleModule();
module.addDeserializer(Step.class, new StepDeserializer());
mapper.registerModule(module);

Or to Step itself (this gives stack overflow, need to work out how to stop subclasses from using the same deserializer perhaps):

@ApiModel(description = "")
@JsonDeserialize(using = StepDeserializer.class)
public class Step  {

Option 2

These annotations on Step itself:

@JsonTypeInfo(use = Id.CUSTOM, property = "step_type", include = As.PROPERTY)
@JsonTypeIdResolver(StepTypeIdResolver.class)

And then this custom type resolver, can't see a way to do it with the built in resolvers:

public class StepTypeIdResolver implements TypeIdResolver {

    private JavaType mBaseType;

    @Override
    public void init(JavaType baseType) {
        mBaseType = baseType;
    }

    @Override
    public String idFromValue(Object value) {
        return idFromValueAndType(value, value.getClass());
    }

    @Override
    public String idFromValueAndType(Object value, Class<?> suggestedType) {
        if (RequestStep.class.equals(suggestedType)) {
            return Step.StepTypeEnum.request.toString();
        } else if (ConditionStep.class.equals(suggestedType)) {
            return Step.StepTypeEnum.condition.toString();
        } else if (PauseStep.class.equals(suggestedType)) {
            return Step.StepTypeEnum.pause.toString();
        }
        return String.valueOf(value);
    }

    @Override
    public String idFromBaseType() {
        return idFromValueAndType(null, mBaseType.getRawClass());
    }

    @Override
    public JavaType typeFromId(String id) {
        Class<? extends Step> clazz = null;
        if (StepTypeEnum.request.toString().equals(id)) {
            clazz = RequestStep.class;
        } else if (StepTypeEnum.condition.toString().equals(id)) {
            clazz = ConditionStep.class;
        } else if (StepTypeEnum.pause.toString().equals(id)) {
            clazz = PauseStep.class;
        }
        return TypeFactory.defaultInstance().constructSpecializedType(mBaseType, clazz);
    }

    @Override
    public JsonTypeInfo.Id getMechanism() {
        return Id.CUSTOM;
    }

}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions