jcazevedo / moultingyaml

Scala wrapper for SnakeYAML

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Case class creation, default parameters don't work

gregakespret opened this issue · comments

scala> import net.jcazevedo.moultingyaml._
import net.jcazevedo.moultingyaml._

scala> import net.jcazevedo.moultingyaml.DefaultYamlProtocol._ // if you don't supply your own protocol
import net.jcazevedo.moultingyaml.DefaultYamlProtocol._

scala> implicit val fooFormat = yamlFormat2(Foo)
fooFormat: net.jcazevedo.moultingyaml.YamlFormat[Foo] = net.jcazevedo.moultingyaml.ProductFormats$$anon$22@4d3fa7b5

scala> "bar: test".parseYaml.convertTo[Foo]
net.jcazevedo.moultingyaml.package$DeserializationException: YamlObject is missing required member 'baz'
	at net.jcazevedo.moultingyaml.package$.deserializationError(package.scala:23)
	at net.jcazevedo.moultingyaml.ProductFormats$class.readField(ProductFormats.scala:1079)
	at net.jcazevedo.moultingyaml.DefaultYamlProtocol$.readField(DefaultYamlProtocol.scala:13)
	at net.jcazevedo.moultingyaml.ProductFormats$$anon$22.read(ProductFormats.scala:200)
	at net.jcazevedo.moultingyaml.ProductFormats$$anon$22.read(ProductFormats.scala:189)
	at net.jcazevedo.moultingyaml.YamlValue.convertTo(YamlValue.scala:10)
	at .<init>(<console>:17)
	at .<clinit>(<console>)
	at .<init>(<console>:7)
	at .<clinit>(<console>)
	at $print(<console>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:734)
	at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:983)
	at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573)
	at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)
	at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568)
	at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:760)
	at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:805)
	at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:717)
	at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:581)
	at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:588)
	at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:591)
	at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:882)
	at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:837)
	at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:837)
	at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135)
	at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:837)
	at scala.tools.nsc.interpreter.ILoop.main(ILoop.scala:904)
	at xsbt.ConsoleInterface.run(ConsoleInterface.scala:69)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:102)
	at sbt.compiler.AnalyzingCompiler.console(AnalyzingCompiler.scala:77)
	at sbt.Console.sbt$Console$$console0$1(Console.scala:23)
	at sbt.Console$$anonfun$apply$2$$anonfun$apply$1.apply$mcV$sp(Console.scala:24)
	at sbt.Console$$anonfun$apply$2$$anonfun$apply$1.apply(Console.scala:24)
	at sbt.Console$$anonfun$apply$2$$anonfun$apply$1.apply(Console.scala:24)
	at sbt.Logger$$anon$4.apply(Logger.scala:90)
	at sbt.TrapExit$App.run(TrapExit.scala:244)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.util.NoSuchElementException: key not found: YamlString(baz)
	at scala.collection.MapLike$class.default(MapLike.scala:228)
	at scala.collection.AbstractMap.default(Map.scala:58)
	at scala.collection.MapLike$class.apply(MapLike.scala:141)
	at scala.collection.AbstractMap.apply(Map.scala:58)
	at net.jcazevedo.moultingyaml.ProductFormats$class.readField(ProductFormats.scala:1076)
	... 44 more

Is there any workaround to this? When case class has default parameters, user still needs to define those in YAML.

Unfortunately that is currently not supported via the yamlFormatX helpers. The best workaround for this is to define your own YamlFormat (see this section for details) which handles the missing values in the YAML document properly.

However, this is something I'd like to add to MoultingYAML in the future, so I'll leave this issue open for the time being.

Found a solution to this, if you're still interested in extending this -

class WithOptionalLongId[T](baseFormatter:YamlFormat[T]) extends YamlFormat[T]{
    import YamlSupport.yamlObjectToMergeable
    def write(r:T) = baseFormatter.write(r)
    def read(yaml:YamlValue)  = {
      baseFormatter.read(yaml.asYamlObject().mergeAfter(Map("id" -> 0L)))
    }
  }

(where the "mergeAfter" is just gluing some extra values)which allows for e.g.

implicit val MyClassFormat: YamlFormat[MyClass]   = new WithOptionalLongId[User](yamlFormat8(MyClass.apply))