Commit 3d3e7deb authored by mokj's avatar mokj
Browse files

Added support for Scala-POJOs with Lists, Option and polymorphism

parent ba1a3ff9
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ lazy val commonSettings = Seq(


val jacksonVersion = "2.7.5"
val jacksonModuleScalaVersion = "2.7.4"
val slf4jVersion = "1.7.7"


@@ -53,7 +54,8 @@ lazy val deps = Seq(
  "org.slf4j" % "slf4j-api" % slf4jVersion,
  "org.scalatest" % "scalatest_2.11" % "2.2.4" % "test",
  "ch.qos.logback" % "logback-classic" % "1.1.3" % "test",
  "com.github.fge" % "json-schema-validator" % "2.2.6" % "test"
  "com.github.fge" % "json-schema-validator" % "2.2.6" % "test",
  "com.fasterxml.jackson.module" % "jackson-module-scala_2.11" % jacksonModuleScalaVersion % "test"
)

lazy val root = (project in file("."))
+43 −37
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ import com.fasterxml.jackson.core.JsonParser.NumberType
import com.fasterxml.jackson.databind.jsonFormatVisitors._
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter
import com.fasterxml.jackson.databind._
import com.fasterxml.jackson.databind.introspect.{AnnotatedClass, JacksonAnnotationIntrospector}
import com.fasterxml.jackson.databind.node.{ArrayNode, JsonNodeFactory, ObjectNode}
import org.slf4j.LoggerFactory

@@ -15,7 +16,7 @@ object JsonSchemaGenerator {
  val JSON_SCHEMA_DRAFT_4_URL = "http://json-schema.org/draft-04/schema#"
}

class JsonSchemaGenerator(rootObjectMapper: ObjectMapper, debug:Boolean = false) {
class JsonSchemaGenerator(val rootObjectMapper: ObjectMapper, debug:Boolean = false) {

  import scala.collection.JavaConversions._

@@ -146,9 +147,13 @@ class JsonSchemaGenerator(rootObjectMapper: ObjectMapper, debug:Boolean = false)
      val itemsNode = JsonNodeFactory.instance.objectNode()
      node.set("items", itemsNode)

      // When processing scala modules we sometimes get a better elementType here than later in itemsFormat
      val preferredElementType:Option[JavaType] = if ( _type.containedTypeCount() >= 1) Some(_type.containedType(0)) else None

      new JsonArrayFormatVisitor with MySerializerProvider {
        override def itemsFormat(handler: JsonFormatVisitable, elementType: JavaType): Unit = {
          l(s"expectArrayFormat - handler: $handler - elementType: $elementType")
        override def itemsFormat(handler: JsonFormatVisitable, _elementType: JavaType): Unit = {
          l(s"expectArrayFormat - handler: $handler - elementType: ${_elementType} - preferredElementType: $preferredElementType")
          val elementType = preferredElementType.getOrElse(_elementType)
          objectMapper.acceptJsonFormatVisitor(elementType, createChild(itemsNode))
        }

@@ -173,6 +178,9 @@ class JsonSchemaGenerator(rootObjectMapper: ObjectMapper, debug:Boolean = false)

    override def expectAnyFormat(_type: JavaType) = {
      log.warn(s"Not able to generate jsonSchema-info for type: ${_type} - probably using custom serializer which does not override acceptJsonFormatVisitor")



      new JsonAnyFormatVisitor {
      }

@@ -241,25 +249,11 @@ class JsonSchemaGenerator(rootObjectMapper: ObjectMapper, debug:Boolean = false)

    case class PolymorphismInfo(typePropertyName:String, subTypeName:String)

    private def extractPolymorphismInfo(clazz:Class[_]):Option[PolymorphismInfo] = {
    private def extractPolymorphismInfo(_type:JavaType):Option[PolymorphismInfo] = {
      // look for @JsonTypeInfo
      var superClass = clazz
      var jsonTypeInfo:JsonTypeInfo = null

      while( jsonTypeInfo == null && superClass != classOf[Object]) {
        jsonTypeInfo = Option(superClass.getDeclaredAnnotation(classOf[JsonTypeInfo])).map {
          ann:JsonTypeInfo => ann
        }.orNull
        if ( jsonTypeInfo == null ) {
          superClass = superClass.getSuperclass
        }
      }

      if ( jsonTypeInfo == null) {
        return None
      }


      val ac = AnnotatedClass.construct(_type, objectMapper.getDeserializationConfig())
      Option(ac.getAnnotations.get(classOf[JsonTypeInfo])).map {
        jsonTypeInfo =>
          if ( jsonTypeInfo.include() != JsonTypeInfo.As.PROPERTY) throw new Exception("We only support polymorphism using jsonTypeInfo.include() == JsonTypeInfo.As.PROPERTY")
          if ( jsonTypeInfo.use != JsonTypeInfo.Id.NAME) throw new Exception("We only support polymorphism using jsonTypeInfo.use == JsonTypeInfo.Id.NAME")

@@ -268,18 +262,17 @@ class JsonSchemaGenerator(rootObjectMapper: ObjectMapper, debug:Boolean = false)

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

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


      Some(PolymorphismInfo(propertyName, subTypeName))
                  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"))


          PolymorphismInfo(propertyName, subTypeName)
      }

    }

@@ -335,7 +328,7 @@ class JsonSchemaGenerator(rootObjectMapper: ObjectMapper, debug:Boolean = false)
            val propertiesNode = JsonNodeFactory.instance.objectNode()
            thisObjectNode.set("properties", propertiesNode)

            extractPolymorphismInfo(_type.getRawClass).map {
            extractPolymorphismInfo(_type).map {
              case pi:PolymorphismInfo =>
                // This class is a child in a polymorphism config..
                // Set the title = subTypeName
@@ -367,7 +360,20 @@ class JsonSchemaGenerator(rootObjectMapper: ObjectMapper, debug:Boolean = false)

                val childNode = JsonNodeFactory.instance.objectNode()

                objectMapper.acceptJsonFormatVisitor(propertyType, createChild(thisPropertyNode))

                val childVisitor = createChild(thisPropertyNode)

                // Workaround for scala lists and so on
                if ( (propertyType.isArrayType || propertyType.isCollectionLikeType) && propertyType.containedTypeCount() >= 1) {
                  val itemType = propertyType.containedType(0)
                  // If visiting a scala list and using default acceptJsonFormatVisitor-apporach,
                  // we get java.lang.Object instead of actual type.
                  // By doing it manually like this it works.

                  childVisitor.expectArrayFormat(itemType).itemsFormat(null, itemType)
                } else {
                  objectMapper.acceptJsonFormatVisitor(propertyType, childVisitor)
                }

                // Check if we should set this property as required
                val rawClass = prop.getType.getRawClass
+165 −67
Original line number Diff line number Diff line
package com.kjetland.jackson.jsonSchema

import com.fasterxml.jackson.annotation.JsonValue
import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo, JsonValue}
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.node.{ArrayNode, ObjectNode}
import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.github.fge.jsonschema.main.JsonSchemaFactory
import com.kjetland.jackson.jsonSchema.testData._
import org.scalatest.{FunSuite, Matchers}
@@ -12,33 +13,42 @@ import scala.collection.JavaConversions._

class JsonSchemaGeneratorTest extends FunSuite with Matchers {

  val objectMapper = new ObjectMapper()
  val _objectMapper = new ObjectMapper()
  val _objectMapperScala = new ObjectMapper()
  _objectMapperScala.registerModule(new DefaultScalaModule)

  List(_objectMapper, _objectMapperScala).foreach {
    om =>
      val simpleModule = new SimpleModule()
      simpleModule.addSerializer(classOf[PojoWithCustomSerializer], new PojoWithCustomSerializerSerializer)
      simpleModule.addDeserializer(classOf[PojoWithCustomSerializer], new PojoWithCustomSerializerDeserializer)
  objectMapper.registerModule(simpleModule)
      om.registerModule(simpleModule)
  }


  val jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper, debug = true)

  val jsonSchemaGenerator = new JsonSchemaGenerator(_objectMapper, debug = true)
  val jsonSchemaGeneratorScala = new JsonSchemaGenerator(_objectMapperScala, debug = true)

  val testData = new TestData{}

  def asPrettyJson(node:JsonNode):String = {
    objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(node)
  def asPrettyJson(node:JsonNode, om:ObjectMapper):String = {
    om.writerWithDefaultPrettyPrinter().writeValueAsString(node)
  }


  // Asserts that we're able to go from object => json => equal object
  def assertToFromJson(o:Any): JsonNode = {
    assertToFromJson(o, o.getClass)
  def assertToFromJson(g:JsonSchemaGenerator, o:Any): JsonNode = {
    assertToFromJson(g, o, o.getClass)
  }

  // Asserts that we're able to go from object => json => equal object
  // deserType might be a class which o extends (polymorphism)
  def assertToFromJson(o:Any, deserType:Class[_]): JsonNode = {
    val json = objectMapper.writeValueAsString(o)
  def assertToFromJson(g:JsonSchemaGenerator, o:Any, deserType:Class[_]): JsonNode = {
    val json = g.rootObjectMapper.writeValueAsString(o)
    println(s"json: $json")
    val jsonNode = objectMapper.readTree(json)
    val r = objectMapper.treeToValue(jsonNode, deserType)
    val jsonNode = g.rootObjectMapper.readTree(json)
    val r = g.rootObjectMapper.treeToValue(jsonNode, deserType)
    assert( o == r)
    jsonNode
  }
@@ -57,11 +67,11 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {

  // Generates schema, validates the schema using external schema validator and
  // Optionally tries to validate json against the schema.
  def generateAndValidateSchema(clazz:Class[_], jsonToTestAgainstSchema:Option[JsonNode] = None):JsonNode = {
    val schema = jsonSchemaGenerator.generateJsonSchema(clazz)
  def generateAndValidateSchema(g:JsonSchemaGenerator, clazz:Class[_], jsonToTestAgainstSchema:Option[JsonNode] = None):JsonNode = {
    val schema = g.generateJsonSchema(clazz)

    println("--------------------------------------------")
    println(asPrettyJson(schema))
    println(asPrettyJson(schema, g.rootObjectMapper))

    assert( JsonSchemaGenerator.JSON_SCHEMA_DRAFT_4_URL == schema.at("/$schema").asText())

@@ -116,29 +126,42 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {
  }

  test("Generate scheme for plain class not using @JsonTypeInfo") {
    val jsonNode = assertToFromJson(testData.classNotExtendingAnything)

    val schema = generateAndValidateSchema((testData.classNotExtendingAnything).getClass, Some(jsonNode))
    def doTest(pojo:Object, clazz:Class[_], g:JsonSchemaGenerator): Unit = {

      val jsonNode = assertToFromJson(g, pojo)
      val schema = generateAndValidateSchema(g, clazz, Some(jsonNode))

      assert( false == schema.at("/additionalProperties").asBoolean())
      assert( schema.at("/properties/someString/type").asText() == "string")
    }

    doTest(testData.classNotExtendingAnything, testData.classNotExtendingAnything.getClass, jsonSchemaGenerator)
    doTest(testData.classNotExtendingAnythingScala, testData.classNotExtendingAnythingScala.getClass, jsonSchemaGeneratorScala)

  }

  test("Generating schema for concrete class which happens to extend class using @JsonTypeInfo") {
    val jsonNode = assertToFromJson(testData.child1)

    val schema = generateAndValidateSchema(testData.child1.getClass, Some(jsonNode))
    def doTest(pojo:Object, clazz:Class[_], g:JsonSchemaGenerator): Unit = {
      val jsonNode = assertToFromJson(g, pojo)
      val schema = generateAndValidateSchema(g, clazz, Some(jsonNode))

      assert( false == schema.at("/additionalProperties").asBoolean())
      assert( schema.at("/properties/parentString/type").asText() == "string")
      assertJsonSubTypesInfo(schema, "type", "child1")
    }

    doTest(testData.child1, testData.child1.getClass, jsonSchemaGenerator)
    doTest(testData.child1Scala, testData.child1Scala.getClass, jsonSchemaGeneratorScala)
  }

  test("Generate schema for regular class which has a property of class annotated with @JsonTypeInfo") {
    val jsonNode = assertToFromJson(testData.pojoWithParent)
    val schema = generateAndValidateSchema(testData.pojoWithParent.getClass, Some(jsonNode))

    // Java
    {
      val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.pojoWithParent)
      val schema = generateAndValidateSchema(jsonSchemaGenerator, testData.pojoWithParent.getClass, Some(jsonNode))

      assert(false == schema.at("/additionalProperties").asBoolean())
      assert(schema.at("/properties/pojoValue/type").asText() == "boolean")
@@ -146,37 +169,66 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {
      assertChild1(schema, "/properties/child/oneOf")
      assertChild2(schema, "/properties/child/oneOf")

    }

    // Scala
    {
      val jsonNode = assertToFromJson(jsonSchemaGeneratorScala, testData.pojoWithParentScala)
      val schema = generateAndValidateSchema(jsonSchemaGeneratorScala, testData.pojoWithParentScala.getClass, Some(jsonNode))

      assert(false == schema.at("/additionalProperties").asBoolean())
      assert(schema.at("/properties/pojoValue/type").asText() == "boolean")

      assertChild1(schema, "/properties/child/oneOf", "Child1Scala")
      assertChild2(schema, "/properties/child/oneOf", "Child2Scala")

    }

  }

  def assertChild1(node:JsonNode, path:String): Unit ={
    val child1 = getNodeViaArrayOfRefs(node, path, "Child1")
  def assertChild1(node:JsonNode, path:String, defName:String = "Child1"): Unit ={
    val child1 = getNodeViaArrayOfRefs(node, path, defName)
    assertJsonSubTypesInfo(child1, "type", "child1")
    assert( child1.at("/properties/parentString/type").asText() == "string" )
    assert( child1.at("/properties/child1String/type").asText() == "string" )
  }

  def assertChild2(node:JsonNode, path:String): Unit ={
    val child2 = getNodeViaArrayOfRefs(node, path, "Child2")
  def assertChild2(node:JsonNode, path:String, defName:String = "Child2"): Unit ={
    val child2 = getNodeViaArrayOfRefs(node, path, defName)
    assertJsonSubTypesInfo(child2, "type", "child2")
    assert( child2.at("/properties/parentString/type").asText() == "string" )
    assert( child2.at("/properties/child2int/type").asText() == "integer" )
  }

  test("Generate schema for super class annotated with @JsonTypeInfo") {
    val jsonNode = assertToFromJson(testData.child1)
    assertToFromJson(testData.child1, classOf[Parent])

    val schema = generateAndValidateSchema(classOf[Parent], Some(jsonNode))
    // Java
    {
      val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.child1)
      assertToFromJson(jsonSchemaGenerator, testData.child1, classOf[Parent])

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

      assertChild1(schema, "/oneOf")
      assertChild2(schema, "/oneOf")
    }

    // Scala
    {
      val jsonNode = assertToFromJson(jsonSchemaGeneratorScala, testData.child1Scala)
      assertToFromJson(jsonSchemaGeneratorScala, testData.child1Scala, classOf[ParentScala])

      val schema = generateAndValidateSchema(jsonSchemaGeneratorScala, classOf[ParentScala], Some(jsonNode))

      assertChild1(schema, "/oneOf", "Child1Scala")
      assertChild2(schema, "/oneOf", "Child2Scala")
    }

  }

  test("primitives") {
    val jsonNode = assertToFromJson(testData.manyPrimitives)
    val schema = generateAndValidateSchema(testData.manyPrimitives.getClass, Some(jsonNode))
    val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.manyPrimitives)
    val schema = generateAndValidateSchema(jsonSchemaGenerator, testData.manyPrimitives.getClass, Some(jsonNode))

    assert( schema.at("/properties/_string/type").asText() == "string" )

@@ -209,15 +261,15 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {

  test("custom serializer not overriding JsonSerializer.acceptJsonFormatVisitor") {

    val jsonNode = assertToFromJson(testData.pojoWithCustomSerializer)
    val schema = generateAndValidateSchema(testData.pojoWithCustomSerializer.getClass, Some(jsonNode))
    val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.pojoWithCustomSerializer)
    val schema = generateAndValidateSchema(jsonSchemaGenerator, testData.pojoWithCustomSerializer.getClass, Some(jsonNode))
    assert( schema.asInstanceOf[ObjectNode].fieldNames().toList == List("$schema")) // Empyt schema due to custom serializer
  }

  test("object with property using custom serializer not overriding JsonSerializer.acceptJsonFormatVisitor") {

    val jsonNode = assertToFromJson(testData.objectWithPropertyWithCustomSerializer)
    val schema = generateAndValidateSchema(testData.objectWithPropertyWithCustomSerializer.getClass, Some(jsonNode))
    val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.objectWithPropertyWithCustomSerializer)
    val schema = generateAndValidateSchema(jsonSchemaGenerator, testData.objectWithPropertyWithCustomSerializer.getClass, Some(jsonNode))
    assert( schema.at("/properties/s/type").asText() == "string")
    assert( schema.at("/properties/child").asInstanceOf[ObjectNode].fieldNames().toList.size == 0)
  }
@@ -225,8 +277,10 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {

  test("pojoWithArrays") {

    val jsonNode = assertToFromJson(testData.pojoWithArrays)
    val schema = generateAndValidateSchema(testData.pojoWithArrays.getClass, Some(jsonNode))
    def doTest(pojo:Object, clazz:Class[_], g:JsonSchemaGenerator): Unit ={

      val jsonNode = assertToFromJson(g, pojo)
      val schema = generateAndValidateSchema(g, clazz, Some(jsonNode))

      assert(schema.at("/properties/intArray1/type").asText() == "array")
      assert(schema.at("/properties/intArray1/items/type").asText() == "integer")
@@ -246,9 +300,14 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {
      assertChild2(schema, "/properties/polymorphismArray/items/oneOf")
    }

    doTest( testData.pojoWithArrays, testData.pojoWithArrays.getClass, jsonSchemaGenerator)
    doTest( testData.pojoWithArraysScala, testData.pojoWithArraysScala.getClass, jsonSchemaGeneratorScala)

  }

  test("recursivePojo") {
    val jsonNode = assertToFromJson(testData.recursivePojo)
    val schema = generateAndValidateSchema(testData.recursivePojo.getClass, Some(jsonNode))
    val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.recursivePojo)
    val schema = generateAndValidateSchema(jsonSchemaGenerator, testData.recursivePojo.getClass, Some(jsonNode))

    assert( schema.at("/properties/myText/type").asText() == "string")

@@ -264,8 +323,8 @@ class JsonSchemaGeneratorTest extends FunSuite with Matchers {
  }

  test("pojo using Maps") {
    val jsonNode = assertToFromJson(testData.pojoUsingMaps)
    val schema = generateAndValidateSchema(testData.pojoUsingMaps.getClass, Some(jsonNode))
    val jsonNode = assertToFromJson(jsonSchemaGenerator, testData.pojoUsingMaps)
    val schema = generateAndValidateSchema(jsonSchemaGenerator, testData.pojoUsingMaps.getClass, Some(jsonNode))

    assert( schema.at("/properties/string2Integer/type").asText() == "object")
    assert( schema.at("/properties/string2Integer/additionalProperties").asBoolean() == true)
@@ -289,6 +348,8 @@ trait TestData {
    c
  }

  val child1Scala = Child1Scala("pv", "cs")

  val child2 = {
    val c = new Child2()
    c.parentString = "pv"
@@ -296,6 +357,8 @@ trait TestData {
    c
  }

  val child2Scala = Child2Scala("pv", 12)

  val pojoWithParent = {
    val p = new PojoWithParent
    p.pojoValue = true
@@ -303,12 +366,16 @@ trait TestData {
    p
  }

  val pojoWithParentScala = PojoWithParentScala(true, child1Scala)

  val classNotExtendingAnything = {
    val o = new ClassNotExtendingAnything
    o.someString = "Something"
    o
  }

  val classNotExtendingAnythingScala = ClassNotExtendingAnythingScala("Something")

  val manyPrimitives = new ManyPrimitives("s1", 1, 2, true, false, true, 0.1, 0.2, MyEnum.B)

  val pojoWithCustomSerializer = {
@@ -324,8 +391,17 @@ trait TestData {
    Array("a1","a2","a3"),
    List("l1", "l2", "l3"),
    List(child1, child2),
    List(child1, child2).toArray
    List(child1, child2).toArray,
    List(classNotExtendingAnything, classNotExtendingAnything)
  )

  val pojoWithArraysScala = PojoWithArraysScala(
    Some(List(1,2,3)),
    List("a1","a2","a3"),
    List("l1", "l2", "l3"),
    List(child1, child2),
    List(child1, child2),
    List(classNotExtendingAnything, classNotExtendingAnything)
  )

  val recursivePojo = new RecursivePojo("t1", List(new RecursivePojo("c1", null)))
@@ -337,3 +413,25 @@ trait TestData {
    )

}


case class PojoWithArraysScala
(
  intArray1:Option[List[Integer]], // We never use array in scala - use list instead to make it compatible with PojoWithArrays (java)
  stringArray:List[String], // We never use array in scala - use list instead to make it compatible with PojoWithArrays (java)
  stringList:List[String],
  polymorphismList:List[Parent],
  polymorphismArray:List[Parent], // We never use array in scala - use list instead to make it compatible with PojoWithArrays (java)
  regularObjectList:List[ClassNotExtendingAnything]
)

case class ClassNotExtendingAnythingScala(someString:String)

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(Array(new JsonSubTypes.Type(value = classOf[Child1Scala], name = "child1"), new JsonSubTypes.Type(value = classOf[Child2Scala], name = "child2")))
trait ParentScala

case class Child1Scala(parentString:String, child1String:String) extends ParentScala
case class Child2Scala(parentString:String, child2int:Int) extends ParentScala

case class PojoWithParentScala(pojoValue:Boolean, child:ParentScala)
 No newline at end of file
+5 −1
Original line number Diff line number Diff line
@@ -12,16 +12,18 @@ public class PojoWithArrays {

    public List<Parent> polymorphismList;
    public Parent[] polymorphismArray;
    public List<ClassNotExtendingAnything> regularObjectList;

    public PojoWithArrays() {
    }

    public PojoWithArrays(int[] intArray1, String[] stringArray, List<String> stringList, List<Parent> polymorphismList, Parent[] polymorphismArray) {
    public PojoWithArrays(int[] intArray1, String[] stringArray, List<String> stringList, List<Parent> polymorphismList, Parent[] polymorphismArray, List<ClassNotExtendingAnything> regularObjectList) {
        this.intArray1 = intArray1;
        this.stringArray = stringArray;
        this.stringList = stringList;
        this.polymorphismList = polymorphismList;
        this.polymorphismArray = polymorphismArray;
        this.regularObjectList = regularObjectList;
    }

    @Override
@@ -36,6 +38,7 @@ public class PojoWithArrays {
        if (!Arrays.equals(stringArray, that.stringArray)) return false;
        if (stringList != null ? !stringList.equals(that.stringList) : that.stringList != null) return false;
        if (polymorphismList != null ? !polymorphismList.equals(that.polymorphismList) : that.polymorphismList != null)
        if (regularObjectList != null ? !regularObjectList.equals(that.regularObjectList) : that.regularObjectList != null)
            return false;
        // Probably incorrect - comparing Object[] arrays with Arrays.equals
        return Arrays.equals(polymorphismArray, that.polymorphismArray);
@@ -48,6 +51,7 @@ public class PojoWithArrays {
        result = 31 * result + Arrays.hashCode(stringArray);
        result = 31 * result + (stringList != null ? stringList.hashCode() : 0);
        result = 31 * result + (polymorphismList != null ? polymorphismList.hashCode() : 0);
        result = 31 * result + (regularObjectList != null ? regularObjectList.hashCode() : 0);
        result = 31 * result + Arrays.hashCode(polymorphismArray);
        return result;
    }