Commit 41908a34 authored by Morten Kjetland's avatar Morten Kjetland
Browse files

Fixes #6 - By using Jackson's SubtypeResolver, we now also supports...

Fixes #6 - By using Jackson's SubtypeResolver, we now also supports Polymorphism using Mixing and RegisterSubtype, in addidtion to @JsonTypeInfo
parent 32c899e0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ Current version: *1.0.7*
**Highlights**

* JSON Schema Draft v4
* Supports polymorphism using **@JsonTypeInfo** and **oneOf**
* Supports polymorphism (**@JsonTypeInfo**, **MixIn**, and **registerSubtypes()**) using JsonSchema's **oneOf**-feature.
* Supports schema customization using **@JsonSchemaDescription**, **@JsonSchemaFormat** and **@JsonSchemaTitle**
* Supports many Javax-validation @Annotations
* Works well with Generated GUI's using [https://github.com/jdorn/json-editor](https://github.com/jdorn/json-editor)
+9 −15
Original line number Diff line number Diff line
@@ -431,16 +431,11 @@ class JsonSchemaGenerator

          val propertyName = jsonTypeInfo.property()

          // must look at the @JsonSubTypes to find what this current class should be called

          val subTypeName:String = Option(ac.getAnnotations.get(classOf[JsonSubTypes])).map {
            ann: JsonSubTypes => ann.value()
              .find {
                t: JsonSubTypes.Type =>
                  t.value() == _type.getRawClass
              }.map(_.name()).getOrElse(throw new Exception(s"Did not find info about the class ${_type.getRawClass} in @JsonSubTypes"))
          }.getOrElse(throw new Exception(s"Did not find @JsonSubTypes"))

          // Must find out what this current class should be called
          val subTypeName: String = objectMapper.getSubtypeResolver.collectAndResolveSubtypesByClass(objectMapper.getDeserializationConfig, ac).toList
            .filter(_.getType == _type.getRawClass)
            .find(p => true) // find first
            .get.getName

          PolymorphismInfo(propertyName, subTypeName)
      }
@@ -449,11 +444,10 @@ class JsonSchemaGenerator

    override def expectObjectFormat(_type: JavaType) = {

      val subTypes: List[Class[_]] = Option(_type.getRawClass.getDeclaredAnnotation(classOf[JsonSubTypes])).map {
        ann: JsonSubTypes => ann.value().map {
          t: JsonSubTypes.Type => t.value()
        }.toList
      }.getOrElse(List())
      val ac = AnnotatedClass.construct(_type, objectMapper.getDeserializationConfig())
      val resolvedSubTypes = objectMapper.getSubtypeResolver.collectAndResolveSubtypesByClass(objectMapper.getDeserializationConfig, ac).toList
      val subTypes: List[Class[_]] = resolvedSubTypes.map( _.getType)
        .filter( c => _type.getRawClass.isAssignableFrom(c) && _type.getRawClass != c)

      if (subTypes.nonEmpty) {
        //l(s"polymorphism - subTypes: $subTypes")
+27 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.github.fge.jsonschema.main.JsonSchemaFactory
import com.kjetland.jackson.jsonSchema.testData._
import com.kjetland.jackson.jsonSchema.testData.mixin.{MixinChild1, MixinModule, MixinParent}
import org.scalatest.{FunSuite, Matchers}

import scala.collection.JavaConversions._
@@ -37,6 +38,9 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {
      om.registerModule(new Jdk8Module )
      om.registerModule(new JodaModule)

      // For the mixin-test
      om.registerModule( new MixinModule)

      om.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
      om.setTimeZone(TimeZone.getDefault())
  }
@@ -629,6 +633,20 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {
    assert(schema.at("/properties/doubleMax/maximum").asInt() == 10)
  }

  test("Polymorphism using mixin") {

    // Java
    {
      val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.mixinChild1)
      assertToFromJson(jsonSchemaGenerator, testData.mixinChild1, classOf[MixinParent])

      val schema = generateAndValidateSchema(jsonSchemaGenerator, classOf[MixinParent], Some(jsonNode))

      assertChild1(schema, "/oneOf", defName = "MixinChild1")
      assertChild2(schema, "/oneOf", defName = "MixinChild2")
    }
  }

}

trait TestData {
@@ -720,6 +738,15 @@ trait TestData {
    "_stringUsingNotNull", "_stringUsingSize", "_stringUsingSizeOnlyMin", "_stringUsingSizeOnlyMax", "_stringUsingPatternA",
    1, 2, 1.0, 2.0
  )

  val mixinChild1 = {
    val c = new MixinChild1()
    c.parentString = "pv"
    c.child1String = "cs"
    c.child1String2 = "cs2"
    c.child1String3 = "cs3"
    c
  }
}


+39 −0
Original line number Diff line number Diff line
package com.kjetland.jackson.jsonSchema.testData.mixin;

import com.fasterxml.jackson.annotation.JsonProperty;

public class MixinChild1 extends MixinParent {

    public String child1String;

    @JsonProperty("_child1String2")
    public String child1String2;

    @JsonProperty(value = "_child1String3", required = true)
    public String child1String3;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof MixinChild1)) return false;
        if (!super.equals(o)) return false;

        MixinChild1 child1 = (MixinChild1) o;

        if (child1String != null ? !child1String.equals(child1.child1String) : child1.child1String != null)
            return false;
        if (child1String2 != null ? !child1String2.equals(child1.child1String2) : child1.child1String2 != null)
            return false;
        return child1String3 != null ? child1String3.equals(child1.child1String3) : child1.child1String3 == null;

    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + (child1String != null ? child1String.hashCode() : 0);
        result = 31 * result + (child1String2 != null ? child1String2.hashCode() : 0);
        result = 31 * result + (child1String3 != null ? child1String3.hashCode() : 0);
        return result;
    }
}
+22 −0
Original line number Diff line number Diff line
package com.kjetland.jackson.jsonSchema.testData.mixin;

public class MixinChild2 extends MixinParent {

    public Integer child2int;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MixinChild2 child2 = (MixinChild2) o;

        return child2int != null ? child2int.equals(child2.child2int) : child2.child2int == null;

    }

    @Override
    public int hashCode() {
        return child2int != null ? child2int.hashCode() : 0;
    }
}
Loading