Git Product home page Git Product logo

argus's Introduction

Argus (船大工)

TravisCI

Scala macros for generating code from Json Schemas. Any structures defined within the schema (such as properties, enums, etc) are used to generate scala code at compile time. Json encoder/decoders are also generated for the Circe Json library.

NB: Why Argus? In keeping with the theme of Argonaut and Circe, Argus (son of Arestor) was the builder of the ship "Argo", on which the Argonauts sailed.

Quick Example

Starting with this Json schema.

{
  "title": "Example Schema",
  "type": "object",
  "definitions" : {
    "Address": {
      "type": "object",
      "properties": {
        "number" : { "type": "integer" },
        "street" : { "type": "string" }
      }
    },
    "ErdosNumber": {
      "type": "integer"
    }
  },
  "properties": {
    "name": {
      "type": "array",
      "items": { "type": "string" }
    },
    "age": {
      "description": "Age in years",
      "type": "integer"
    },
    "address" : { "$ref" : "#/definitions/Address" },
    "erdosNumber" : { "$ref" : "#/definitions/ErdosNumber" }
  }
}

We can use the @fromSchemaResource macro to generate case classes for (Root, Address)

import argus.macros._
import io.circe._
import io.circe.syntax._

@fromSchemaResource("/simple.json", name="Person")
object Schema
import Schema._
import Schema.Implicits._

val json = """
   |{
   |  "name" : ["Bob", "Smith"],
   |  "age" : 26,
   |  "address" : {
   |    "number" : 31,
   |    "street" : "Main St"
   |  },
   |  "erdosNumber": 123
   |}
  """.stripMargin

// Decode into generated case class
val person = parser.decode[Person](json).toOption.get

// Update address 
val address = Address(number=Some(42), street=Some("Alt St"))
val newPerson = person.copy(address=Some(address))

// Encode base to json
newPerson.asJson 

Many more examples here

Rules

Supported constructs

Object templates (i.e. classes)

JsonGenerated Scala
{
  "properties" : { 
    "name" : { "type" : "string" },
    "age"  : { "type" : "integer" }
  },
  "required" : ["name"]
}
case class Root(name: String, age: Option[Int] = None)

Basic types

json typejson formatGenerated Scala type
string
*
String
string
uuid
java.util.UUID
string
date-time
java.time.ZonedDateTime
integer
* | int32
Int
integer
int64
Long
integer
int16
Short
integer
int8
Byte
number
* | double
Double
number
single
Float
boolean
*
Boolean
null
*
Null

Definitions (i.e. common class definitions)

JsonGenerated Scala
{
  "definitions" : { 
    "Person" : { ... },
    ...
  }
  "properties" : { 
    "person" : { "$ref" : "#/definitions/Person" }
  }
}
case class Person(...)
case class Root(person: Option[Person] = None)

OneOf (i.e. type A or B)

JsonGenerated Scala
{
  "oneOf": [
    { "$ref" : "#/definitions/Address" },
    { "type" : "number" }
  ]
}
@union sealed trait RootUnion
case class RootAddress(...) extends RootUnion
case class RootDouble(...) extends RootUnion

Enums

JsonGenerated Scala
{
  "properties": { 
    "countries" : { "enum" : ["NZ", "US", "UK"] }
  }
}
@enum sealed trait Countries
object CountriesEnum {
  case object NZ(...) extends Countries
  case object US(...) extends Countries
  ...
}
case class Root(countries: Option[Countries] = None)

Arrays

JsonGenerated Scala
{
  "properties": { 
    "places" : { "items" : { "type": "string" } }
  }
}
case class Root(places: Option[List[String]] = None)

Any Types (i.e. when a field can take arbitrary values)

JsonGenerated Scala
{
  "properties": { 
    "values" : { }
  }
}
case class Values(x: Any)
case class Root(values: Option[Values] = None)

Unsupported

  • Only Circe encoders/decoders are supported, although the skeleton is laid out for adding support for other Json libraries.
  • anyOf / allOf. Should be simple to add, just haven't done it yet.
  • default. Schemas can specify the default value to use for a field. Currently we just ignore these.
  • not. Not sure how you could specify this in a type language. Needs more thoughts
  • additionalProperties, and additionalItems. Json schema lets you specify free-form properties too. These are unsupported for now (although I think this should be easy now there's supprot for Any types)
  • patternProperties. What should be the objects fields in this case? Maybe we just make it a Map?
  • dependencies. Dependencies can also extend the schema... sigh.
  • Any of the validation-only info, such as maxItems, since it doesn't contribute to the structure.

And lastly: We only generate code from json schemas, but we can't generate json-schema from code. This is fully possible, but requires work ;)

There's still a lot to do! Looking for contributors to address any of above.

Usage Tips

  1. All macros support arguments debug=true and outPath="...". debug causes the generated code to be dumped to stdout, and outPath causes the generated code to be written to a file. The optional argument outPathPackage allows to specify a package name for the output file.

    @fromSchemaResource("/simple.json", debug=true, outPath="/tmp/Simple.Scala", outPathPackage="argus.simple")
    object Test
  2. You can generate code from inline json schemas. Also supported are fromSchemaInputStream and fromSchemaURL too.

    @fromSchemaJson("""
    {
      "properties" : { 
        "name" : { "type" : "string" },
        "age"  : { "type" : "integer" }
      },
      "required" : ["name"]
    }
    """)
    object Schema
  3. You can name the root class that is generated via the name="..." argument.

    @fromSchemaResource("/simple.json", name="Person")
    object Schema
    import Schema.Person
  4. Within the object we also generate json encoder/decoder implicit variables, but you need to import them into scope.

    @fromSchemaResource("/simple.json", name="Person")
    object Schema
    import Schema._
    import Schema.Implicits._
    
    Person(...).asJson
  5. You can override specific Encoders/Decoders. All implicits are baked into a trait called LowPriorityImplicits. Rather than importing Foo.Implicits you can make your own implicits object that extends this and provides overrides. For example:

    @fromSchemaResource("/simple.json")
    object Foo
    import Foo._
    
    object BetterImplicits extends Foo.LowPriorityImplicits {
      implicit val myEncoder: Encoder[Foo.Root] =   ... 
      implicit val betterDecoder: Decoder[Foo.Root] = ...
    }
    import BetterImplicits._
  6. Free form Json (we call them Any types above) are quite common within Json schemas. These are fields that are left open to take any kind of Json chunk (maybe for additional properties, or data, etc). Unfortunately they presents a challenge in a strongly typed language, such as Scala, where we always need some kind of type.

    The approach we've taken is to wrap these chunks in their own case class which has a single field of type Any. This also allows you to override the encode/decoders for that type (Root.Data in this example) with something more custom if required.

    @fromSchemaJson("""
    {
      "type": "object",
      "properties" : { 
        "data" : { "type": "array", "items": { } }
      }
    }
    """)
    object Schema
    import Schema._
    import Schema.Implicits._
    
    val values = List( Root.Data(Map("size" -> 350, "country" -> "US")), Root.Data(Map("size" -> 350, "country" -> "US")) )
    Root(data=Some(values))

    The default encoder/decoder (as shown in the code example above) works if your types are:

    • Primitive types: Boolean, Byte, Short, Int, Long, Float, Double
    • Primate arrays (Array[Byte], Array[Int], etc)
    • Seq[Any] (and subclasses) where Any needs to be one of the types in this list
    • Maps[String, Any], where Any needs to be one of the types in this list.

    Or, in other words, you can't stick arbitrary objects in the Any wrapper and expect their encoders/decoders to get picked up. If you need that then you'll have to override the default encoder/decoder for this type.

argus's People

Contributors

aishfenton avatar dbtsai avatar jeremyrsmith avatar jhnsmth avatar marcprux avatar mehmet-ali-arslan avatar rogermenezes avatar tscholak avatar tugdualsarazin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

argus's Issues

Any plans to cut a release with Circe 0.9?

The Vegas library can't be updated to Circe 0.9 until this library is, the current master branch already has Circe 0.9 and clearly there is no development in progress. @aishfenton any chance you would be willing to cut a release for this project in it's current state?

Support extending from a custom trait

We would like to pattern match against multiple Argus-generated case classes which all extend from a common sealed trait. Is there a way to make the generated code extend a predefined trait?

Error compiling the FHIR JSON schema

I think a very good test-case is the FHIR schema, for example, this version:
http://hl7.org/fhir/STU3/fhir.schema.json.zip

There I use the file fhir.schema.json as stated in the doc:

@fromSchemaResource("/fhir/fhir.schema.json")
object SchemaDef

and results in the compiler error:

SchemaDef.scala:5:2: ResourceListParameters is already defined as case class ResourceListParameters

I have to manually remove the resourceList at the bottom of the file and then compiles.

Eliminate intermediary "*Union" wrappers for object-typed oneOf traits

The following schema:

{
  "$ref": "#/definitions/FooBar",
  "definitions": {
    "FooBar": {
      "oneOf": [
        { "$ref": "#/definitions/Foo" },
        { "$ref": "#/definitions/Bar" }
      ]
    },
    "Foo": {
      "type": "object",
      "properties": {
        "foo": {
          "type": "string"
        }
      }
    },
    "Bar": {
      "type": "object",
      "properties": {
        "bar": {
          "type": "integer"
        }
      }
    }
  }
}

will yield this:

sealed trait FooBarUnion
case class FooBarFoo(x: Foo) extends FooBarUnion
case class FooBarBar(x: Bar) extends FooBarUnion
case class Foo(foo: Option[String] = None)
case class Bar(bar: Option[Int] = None)

It would be nice if the intermediary wrappers could be eliminated and this could be simplified to:

sealed trait FooBarUnion
case class Foo(foo: Option[String] = None) extends FooBarUnion
case class Bar(bar: Option[Int] = None) extends FooBarUnion

Schemas in resources not available to macros at compile time

When trying to use the @fromSchemaResource macro, I got an Input stream is null exception even though the schema was in the resources directory and the path was correct. I found that this was due to the resources not being available at compile time when the macro runs.

Adding the following to my build.sbt solved the issue:

unmanagedClasspath in Compile ++= (unmanagedResources in Compile).value

Perhaps it would be good to mention this in the documentation.

Support parsing Swagger Specification

I'm interested in writing a Scala competitor to swagger-codegen using Argus and scala.meta.

Currently unsupported:

  • anyOf / allOf "Should be simple to add, just haven't done it yet."

allOf is all that's needed, but anyOf should be implemented for completeness

  • default "Schemas can specify the default value to use for a field. Currently we just ignore these."

Needed, but would prefer to implement default values in a way that doesn't leverage case class default parameters. Open to discussion, but it violates a wartremover wart (and a convincing argument against default properties).

  • not "Not sure how you could specify this in a type language. Needs more thoughts"

As with default values, validation could be generated as a separate structure that could be optionally applied by consumers of a generated argus case class schema.

  • additionalProperties / additionalItems "Json schema lets you specify free-form properties too. These are unsupported for now (although I think this should be easy now there's supprot(sic) for Any types)"

Only additionalProperties are required, but additionalItems should be supported as well.

  • patternProperties "What should be the objects fields in this case? Maybe we just make it a Map?"

Unnecessary

  • dependencies. "Dependencies can also extend the schema... sigh."

Doesn't seem necessary

  • Any of the validation-only info, such as maxItems, since it doesn't contribute to the structure.

Could be combined with the validation structure generated by not


I'd be interested in feedback for some of these ideas before I start trying to implement some of them.

AnyOf and AllOf support

I'm interested in working with the implementation of this, since I would like to use it with Vegas to support Vega (and newer versions of Vega-Lite) to be able to use some of the more advanced visualizations that Vega-Lite lacks.

Is there anyone actively working on this? Or would it be OK if I work on it?

I would be using the proposal from https://github.com/joelittlejohn/jsonschema2pojo/wiki/Proposal-for-allOf,-anyOf-and-oneOf as a guide to implement this on Scala, unless there's an objection to it.

Running sbt on fresh clone crashes

A fresh clone and running sbt produces a bunch of errors:

[ERROR] Failed to construct terminal; falling back to unsupported
java.lang.NumberFormatException: For input string: "0x100"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.valueOf(Integer.java:766)
	at jline.internal.InfoCmp.parseInfoCmp(InfoCmp.java:59)
	at jline.UnixTerminal.parseInfoCmp(UnixTerminal.java:233)
	at jline.UnixTerminal.<init>(UnixTerminal.java:64)
	at jline.UnixTerminal.<init>(UnixTerminal.java:49)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at jline.TerminalFactory.getFlavor(TerminalFactory.java:209)
	at jline.TerminalFactory.create(TerminalFactory.java:100)
	at jline.TerminalFactory.get(TerminalFactory.java:184)
	at jline.TerminalFactory.get(TerminalFactory.java:190)
	at sbt.ConsoleLogger$.ansiSupported(ConsoleLogger.scala:123)
	at sbt.ConsoleLogger$.<init>(ConsoleLogger.scala:117)
	at sbt.ConsoleLogger$.<clinit>(ConsoleLogger.scala)
	at sbt.GlobalLogging$.initial(GlobalLogging.scala:43)
	at sbt.StandardMain$.initialGlobalLogging(Main.scala:64)
	at sbt.StandardMain$.initialState(Main.scala:73)
	at sbt.xMain.run(Main.scala:29)
	at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:109)
	at xsbt.boot.Launch$.withContextLoader(Launch.scala:128)
	at xsbt.boot.Launch$.run(Launch.scala:109)
	at xsbt.boot.Launch$$anonfun$apply$1.apply(Launch.scala:35)
	at xsbt.boot.Launch$.launch(Launch.scala:117)
	at xsbt.boot.Launch$.apply(Launch.scala:18)
	at xsbt.boot.Boot$.runImpl(Boot.scala:56)
	at xsbt.boot.Boot$.main(Boot.scala:18)
	at xsbt.boot.Boot.main(Boot.scala)

[info] Loading project definition from /home/bravegag/code/Argus/project
[info] Set current project to Argus (in build file:/home/bravegag/code/Argus/)
[ERROR] Failed to construct terminal; falling back to unsupported
java.lang.NumberFormatException: For input string: "0x100"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.valueOf(Integer.java:766)
	at jline.internal.InfoCmp.parseInfoCmp(InfoCmp.java:59)
	at jline.UnixTerminal.parseInfoCmp(UnixTerminal.java:233)
	at jline.UnixTerminal.<init>(UnixTerminal.java:64)
	at jline.UnixTerminal.<init>(UnixTerminal.java:49)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at jline.TerminalFactory.getFlavor(TerminalFactory.java:209)
	at jline.TerminalFactory.create(TerminalFactory.java:100)
	at jline.TerminalFactory.get(TerminalFactory.java:184)
	at jline.TerminalFactory.get(TerminalFactory.java:190)
	at sbt.JLine$.sbt$JLine$$terminal(LineReader.scala:85)
	at sbt.JLine$.withTerminal(LineReader.scala:88)
	at sbt.JLine$.usingTerminal(LineReader.scala:96)
	at sbt.JLine$.createReader(LineReader.scala:102)
	at sbt.FullReader.<init>(LineReader.scala:132)
	at sbt.BasicCommands$$anonfun$shell$1.apply(BasicCommands.scala:184)
	at sbt.BasicCommands$$anonfun$shell$1.apply(BasicCommands.scala:181)
	at sbt.Command$$anonfun$command$1$$anonfun$apply$1.apply(Command.scala:30)
	at sbt.Command$$anonfun$command$1$$anonfun$apply$1.apply(Command.scala:30)
	at sbt.Command$.process(Command.scala:93)
	at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:96)
	at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:96)
	at sbt.State$$anon$1.process(State.scala:184)
	at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:96)
	at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:96)
	at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
	at sbt.MainLoop$.next(MainLoop.scala:96)
	at sbt.MainLoop$.run(MainLoop.scala:89)
	at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:68)
	at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:63)
	at sbt.Using.apply(Using.scala:24)
	at sbt.MainLoop$.runWithNewLog(MainLoop.scala:63)
	at sbt.MainLoop$.runAndClearLast(MainLoop.scala:46)
	at sbt.MainLoop$.runLoggedLoop(MainLoop.scala:30)
	at sbt.MainLoop$.runLogged(MainLoop.scala:22)
	at sbt.StandardMain$.runManaged(Main.scala:57)
	at sbt.xMain.run(Main.scala:29)
	at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:109)
	at xsbt.boot.Launch$.withContextLoader(Launch.scala:128)
	at xsbt.boot.Launch$.run(Launch.scala:109)
	at xsbt.boot.Launch$$anonfun$apply$1.apply(Launch.scala:35)
	at xsbt.boot.Launch$.launch(Launch.scala:117)
	at xsbt.boot.Launch$.apply(Launch.scala:18)
	at xsbt.boot.Boot$.runImpl(Boot.scala:56)
	at xsbt.boot.Boot$.main(Boot.scala:18)
	at xsbt.boot.Boot.main(Boot.scala)

[ERROR] Failed to construct terminal; falling back to unsupported
java.lang.NumberFormatException: For input string: "0x100"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.valueOf(Integer.java:766)
	at jline.internal.InfoCmp.parseInfoCmp(InfoCmp.java:59)
	at jline.UnixTerminal.parseInfoCmp(UnixTerminal.java:233)
	at jline.UnixTerminal.<init>(UnixTerminal.java:64)
	at jline.UnixTerminal.<init>(UnixTerminal.java:49)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at jline.TerminalFactory.getFlavor(TerminalFactory.java:209)
	at jline.TerminalFactory.create(TerminalFactory.java:100)
	at jline.TerminalFactory.get(TerminalFactory.java:184)
	at jline.TerminalFactory.get(TerminalFactory.java:190)
	at jline.console.ConsoleReader.<init>(ConsoleReader.java:240)
	at jline.console.ConsoleReader.<init>(ConsoleReader.java:232)
	at jline.console.ConsoleReader.<init>(ConsoleReader.java:220)
	at sbt.JLine$$anonfun$createReader$1.apply(LineReader.scala:103)
	at sbt.JLine$$anonfun$createReader$1.apply(LineReader.scala:102)
	at sbt.JLine$$anonfun$usingTerminal$1.apply(LineReader.scala:98)
	at sbt.JLine$$anonfun$usingTerminal$1.apply(LineReader.scala:96)
	at sbt.JLine$.withTerminal(LineReader.scala:89)
	at sbt.JLine$.usingTerminal(LineReader.scala:96)
	at sbt.JLine$.createReader(LineReader.scala:102)
	at sbt.FullReader.<init>(LineReader.scala:132)
	at sbt.BasicCommands$$anonfun$shell$1.apply(BasicCommands.scala:184)
	at sbt.BasicCommands$$anonfun$shell$1.apply(BasicCommands.scala:181)
	at sbt.Command$$anonfun$command$1$$anonfun$apply$1.apply(Command.scala:30)
	at sbt.Command$$anonfun$command$1$$anonfun$apply$1.apply(Command.scala:30)
	at sbt.Command$.process(Command.scala:93)
	at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:96)
	at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:96)
	at sbt.State$$anon$1.process(State.scala:184)
	at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:96)
	at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:96)
	at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
	at sbt.MainLoop$.next(MainLoop.scala:96)
	at sbt.MainLoop$.run(MainLoop.scala:89)
	at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:68)
	at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:63)
	at sbt.Using.apply(Using.scala:24)
	at sbt.MainLoop$.runWithNewLog(MainLoop.scala:63)
	at sbt.MainLoop$.runAndClearLast(MainLoop.scala:46)
	at sbt.MainLoop$.runLoggedLoop(MainLoop.scala:30)
	at sbt.MainLoop$.runLogged(MainLoop.scala:22)
	at sbt.StandardMain$.runManaged(Main.scala:57)
	at sbt.xMain.run(Main.scala:29)
	at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:109)
	at xsbt.boot.Launch$.withContextLoader(Launch.scala:128)
	at xsbt.boot.Launch$.run(Launch.scala:109)
	at xsbt.boot.Launch$$anonfun$apply$1.apply(Launch.scala:35)
	at xsbt.boot.Launch$.launch(Launch.scala:117)
	at xsbt.boot.Launch$.apply(Launch.scala:18)
	at xsbt.boot.Boot$.runImpl(Boot.scala:56)
	at xsbt.boot.Boot$.main(Boot.scala:18)
	at xsbt.boot.Boot.main(Boot.scala)

definitions beginning with lowercase letter don't compile

I noticed that definitions in the JSON schema must be capitalized for Argus to work. For this example adapted from the Argus readme:

package test

import argus.macros._
import io.circe._
import io.circe.syntax._

@fromSchemaJson("""
{
  "title": "Example Schema",
  "type": "object",
  "definitions" : {
    "address": {
      "type": "object",
      "properties": {
        "number" : { "type": "integer" },
        "street" : { "type": "string" }
      }
    },
    "ErdosNumber": {
      "type": "integer"
    }
  },
  "properties": {
    "name": {
      "type": "array",
      "items": { "type": "string" }
    },
    "age": {
      "description": "Age in years",
      "type": "integer"
    },
    "address" : { "$ref" : "#/definitions/address" },
    "erdosNumber" : { "$ref" : "#/definitions/ErdosNumber" }
  }
}
  """, name = "Person")
object Schema

object TestArgus {

  import Schema._
  import Schema.Implicits._

  val json = """
   |{
   |  "name" : ["Bob", "Smith"],
   |  "age" : 26,
   |  "address" : {
   |    "number" : 31,
   |    "street" : "Main St"
   |  },
   |  "erdosNumber": 123
   |}
  """.stripMargin

  // Decode into generated case class
  val person = parser.decode[Person](json).right.toOption.get

  // Update address 
  val personAddress = address(number = Some(42), street = Some("Alt St"))
  val newPerson = person.copy(address = Some(personAddress))

  // Encode base to json
  newPerson.asJson

}

I get a compile error:

[error] .../src/main/scala/test/TestArgus.scala:7: not found: type address
[error] @fromSchemaJson("""
[error]  ^

If I change the schema to use "Address" (and also the case class name to match), everything works. Is this intended?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.