What I can't work out is how to combine these two ...
# squarelibraries
m
What I can't work out is how to combine these two features, so that my
moshi
variable knows about
BlackjackHand
and has an adapter for it. I can't call
moshi.adapter
until I have called
.build()
, and once I've called
.build()
I can't work out how to add another adapter to the set.
g
I haven't used it myself but maybe
moshi.newBuilder().
would do
m
Ooh, I think that might be the answer. Will give it a try.
...nope.
java.lang.IllegalArgumentException: Platform class java.math.BigDecimal (with no annotations) requires explicit JsonAdapter to be registered
Oh, hang on. I know why that's happening.
OK, still not working.
java.lang.IllegalArgumentException: Expected at least one @ToJson or @FromJson method on com.squareup.moshi.JsonAdapter$2
m
Are you trying to make a custom adapter ? If yes, it should have
@ToJson
or
@FromJson
method. https://github.com/square/moshi#custom-type-adapters
m
No, I'm trying to get Moshi to make an adapter using Moshi.adapter, and use that with my own adapters.
m
Moshi has builtin adapters either from reflection or codegen (which I prefer). These will serialize/deserialize objects using standard strategies
If you need custom serialization/deserialization logic then you need a custom adapter
What do you mean by "use that with my own adapters" ?
m
See description in main channel.
I've opened up an issue too https://github.com/square/moshi/issues/881
m
Does
BlackjackHand
contain
BigDecimal
?
m
Assume it doesn't, as that's kind of a side issue which I already fixed.
m
I can't understand what you're trying to do. The error is pretty explicit, you should have @FromJson or @ToJson methods in both ZonedDateTimeAdapter and BigDecimalAdapter
m
I do
m
ah !
weird
m
Those classes both work fine in isolation
but
mosh = moshi.newBuilder().add(...).build()
seems to lose all the adapters that were in moshi already.
m
Ah, got it,
mosh.newBuilder.add(jsonAdapter)
, the jsonAdapter is an instance here, right ?
Pretty sure you should pass a class
m
Ahhh...
m
ah no forget it
m
...yeah, the adapters are actually `object`s
m
But I'm not sure you can pass jsonAdapter like this
m
It doesn't seem to specify either way in the documentation.
m
It's designed to be configured once and then consumed
m
The README shows an example of using
moshi.adapter
, and an example of using a custom class with @To/FromJson annotations, but it doesn't give an example of both.
m
You shouldn't need to add
jsonAdapter
to your moshiBuilder
To do both, you register your @To/FromJson when creating the moshi instance. Then you call moshi.adapter() to get an actual adapter instance for your specific type. This adapter will delegate to the registered adapters as needed
Copy code
val moshi = Moshi.Builder()
  .add(ZonedDateTimeAdapter)
  .add(BigDecimalAdapter)
  .add(KotlinJsonAdaptorFactory())
  .build()

val jsonAdapter = moshi.adapter(BlackjackHand::class.java);
String json = jsonAdapter.toJson(blackjackHand);
m
I've got a complete runnable example of it not working...
Hang on...
m
moshi.newBuilder().add(jsonAdapter)
👈 Don't do this
m
So how do I add both jsonAdapter and BigDecimalAdapter to the same moshi instance?
m
It should work fine if you just omit this part
Copy code
var moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.add(BigDecimalAdapter)
.build()

    val obj = Bet(BigDecimal.TEN, BlackjackHand("4H 10D"))

    println(moshi.adapter(Bet::class.java).toJson(obj))
Moshi will create a reflection adapter that will use your BigDecimalAdapter for the BigDecimal type
m
So basically if you create an adapter implicitly later on via
moshi.adapter()
, that adapter is available to the Moshi instance without having to explicitly add it?
m
yes, it's provided by moshi, it's always available
It's the
KotlinJsonAdapterFactory
magic
m
It seems like JsonAdapter is being used for two different things here: (1) to provide conversion to/from JSON, when added using Builder().add(...); and (2) to provide type safety when asking moshi to convert an object to/from JSON, via moshi.adapter(...).toJson(...)
m
Yea, in a way it's used in two different ways
The way I see it, you have elementary adapters that you pass to Moshi.Builder().add() and then Moshi take these and constructs an "advanced" adapter that can convert the complex type you're asking for with moshi.adapter(ComplexType::class.java)
m
Right. That's what confused me. The call to moshi.adapter isn't really creating an adapter like you'd add with
.add
, rather it's telling Moshi the full type signature so that it can then construct what it needs to serialize/deserialize.
m
Yep
m
OK, I've updated my issue on Github with some explanation, in case there are other confused people out there.
Kotlin has been quite an adventure, what with the rather sparse documentation...
Do you happen to know if moshi.adapter(...) is an expensive operation or not? i.e. is it OK to run it every time I need to serialize an object, or should I keep one around and use it repeatedly?
m
Not sure but what I would definitely do if I were you is look into CodeGen instead of using reflection. Reflection is known to be slow and adds a few MB to your dependencies.
Or if you feel adventurous, look into kotlinx.serialization and you could reuse your serialization code on ios and chrome !
(moshi is JVM only)
m
I tried codegen but I get problems with clashing versions of jar files reported
m
Ok weird.
m
Oh, just fixed that too -- I had moshi-kotlin and moshi-kotlin-codegen both in the dependencies
Right, will try codegen then.
👍 1
(Though right now my bottleneck is JDBC.)
Copy code
w: Runtime JAR files in the classpath should have the same version. These files were found in the classpath:
    /home/meta/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.40/2d1d0a2f27fd060787075c69113846803fc27734/kotlin-stdlib-jdk8-1.3.40.jar (version 1.3)
    /home/meta/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.40/2995de8f68444ad47f29e7b59962ac31e6301d7e/kotlin-stdlib-jdk7-1.3.40.jar (version 1.3)
    /home/meta/.gradle/caches/modules-2/files-2.1/me.eugeniomarletti.kotlin.metadata/kotlin-compiler-lite/1.0.3-k-1.2.40/a16e967c276379eaf6e1fa03af77664f003db947/kotlin-compiler-lite-1.0.3-k-1.2.40.jar (version 1.2)
    /home/meta/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.40/b8a521c687329303778548e2f09b0ba5b2665236/kotlin-stdlib-1.3.40.jar (version 1.3)
    /home/meta/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.40/ff8f3da514fc2877d1303d55e22d6da8156c29fb/kotlin-stdlib-common-1.3.40.jar (version 1.3)
w: Some runtime JAR files in the classpath have an incompatible version. Consider removing them from the classpath
Don't understand how these are incompatible with each other, or for that matter why I'm ending up with jdk7 when I have jvmTarget set to 1.8
m
jvmTarget most likely tells the kotlin compiler to produce java8 bytecode. That doesn't prevent dependencies to pull different versions of the stdlib.
./gradlew dependencies might or might not help
m
The jars in the error message are all version 1.3.41 except for kotlin-compiler-lite
d
jdk7
is a transitive dependency of
jdk8
270 Views