Skip to content
This repository was archived by the owner on Jan 22, 2019. It is now read-only.

Support @JsonSubTypes in schema generation and serialization #28

Closed
osi opened this issue Nov 24, 2015 · 2 comments
Closed

Support @JsonSubTypes in schema generation and serialization #28

osi opened this issue Nov 24, 2015 · 2 comments

Comments

@osi
Copy link

osi commented Nov 24, 2015

I'd like to get the JsonSubTypes annotation working for schema generation and serialization.

For schema generation, I think it should be a union of all the possible sub-types.

For serialization, if the configuration is such that the type name would be included as a property, ignore that, since it will be included as the name of the record type.

I have written some failing tests, but it is unclear to me how to proceed, since the TypeIdResolver doesn't provided a way to interrogate all possible types.

package com.fasterxml.jackson.dataformat.avro;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apache.avro.Schema;
import org.junit.Test;

import java.util.Arrays;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class JsonTypeInfoTest {

    @Test
    public void testGenerateSchemaForSubTypes() throws Exception {
        AvroMapper mapper = new AvroMapper();
        AvroSchema schema = mapper.schemaFor(Thing.class);

        Schema stringOrNull = Schema.createUnion(Arrays.asList(Schema.create(Schema.Type.NULL), Schema.create(Schema.Type.STRING)));
        Schema thingOne = Schema.createRecord(
                ThingOne.class.getSimpleName(),
                "Schema for " + ThingOne.class.getName(),
                ThingOne.class.getPackage().getName(),
                false);
        thingOne.setFields(Arrays.asList(
                new Schema.Field("favoriteColor", stringOrNull, null, null),
                new Schema.Field("name", stringOrNull, null, null)
        ));

        Schema thingTwo = Schema.createRecord(
                ThingTwo.class.getSimpleName(),
                "Schema for " + ThingTwo.class.getName(),
                ThingTwo.class.getPackage().getName(),
                false);
        thingTwo.setFields(Arrays.asList(
                new Schema.Field("favoriteFood", stringOrNull, null, null),
                new Schema.Field("name", stringOrNull, null, null)
        ));

        Schema expected = Schema.createUnion(Arrays.asList(thingOne, thingTwo));
        assertEquals(expected, schema.getAvroSchema());
    }

    @Test
    public void testSerializeSubType() throws Exception {
        AvroMapper mapper = new AvroMapper();
        AvroSchema schema = mapper.schemaFor(Thing.class);

        assertNotNull(mapper.writer(schema).writeValueAsBytes(new ThingOne("hello", "blue")));
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.PROPERTY,
            property = "@type")
    @JsonSubTypes({
            @JsonSubTypes.Type(value = ThingOne.class, name = "one"),
            @JsonSubTypes.Type(value = ThingTwo.class, name = "two")
    })
    public interface Thing {
        @JsonProperty
        String name();
    }

    public static class ThingOne implements Thing {
        public final String favoriteColor;
        private final String name;

        public ThingOne(String name, String favoriteColor) {
            this.name = name;
            this.favoriteColor = favoriteColor;
        }

        @Override
        public String name() {
            return name;
        }
    }

    public static class ThingTwo implements Thing {
        public final String favoriteFood;
        private final String name;

        public ThingTwo(String name, String favoriteFood) {
            this.name = name;
            this.favoriteFood = favoriteFood;
        }

        @Override
        public String name() {
            return name;
        }
    }
}
@cowtowncoder
Copy link
Member

This is tough one. I agree in that union of subtypes is probably the way to go. I think JsonGenerator and JsonParser have support for so-called "native" object and type ids, and perhaps it would then be possible to actually subvert this to be used for implicit/off-band type ids? If you have time perhaps you might want to have a look at:

  • JsonGenerator.writeTypeId(Object) (and canWriteTypeId() to expose capability)
  • JsonParser.getTypeId() (and canReadTypeId())

as I think combination of these might actually work, when combined with schema lookup to do translation. It is a round-about way of doing things of course, and there may be gaps. But it seems like it just might be possible either as-is, or with possible minor fixes.

@cowtowncoder
Copy link
Member

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants