Is there something like this already available?
If not, do you think it is feasible with a customer JsonQualifier annotation?
{
"id": "abc",
"name": "example",
"other": "foo"
}
And parse this with:
interface Base {
val id: String
val name: String
}
@JsonClass(generateAdapter = true)
data class BaseImpl(
override val id: String,
override val name: String
): Base
@JsonClass(generateAdapter = true)
data class MyType(
漏JsonEmbed(BaseImpl::class)
private val base: Base,
val other: String
): Base by base
I've several models with common parts and I'm looking for ways to reduce the code repetition through the codebase by splitting my models into smaller classes and sharing them like the example above.
Thanks
You can do this like so:
val moshi = Moshi.Builder()
.add(object : JsonAdapter.Factory {
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*> {
return if (type == Base::class.java) return moshi.adapter(BaseImpl::class.java) else null
}
})
If your type can have different subtypes, you can use the PolymorphicJsonAdapter for this use case
val moshi = Moshi.Builder()
.add(PolymorphicJsonAdapterFactory.of(Base::class.java, "id")
.withSubtype(BaseImpl::class.java, "<yourIdForThisHere>"))
.build()
I do wonder if we should maybe have something similar to Dagger's "binds" semantic, where Moshi.Builder could have a method to specify something like this
public Builder bind(Type from, Type to) {
// Check assignability?
// ...
}
That could be used to define a sort of default binding between simple types. Thoughts @swankjesse @rharter @JakeWharton?
Hum,
I guess my Base example wasn't good enough.
I know about the PolymorphicJsonAdapterFactory, but that is NOT what I want.
I want to be able to define complex structures by smaller objects that I combine with the by kotlin keyword.
Basically writing code and pojo by composition.
say
interface Entity {
val uuid: String
val created: Date
val lastUpdate: Date
}
interface InternalMetadata {
val name: String
val description: String
val image: String
}
interface I18N {
val content: Map<Locale, Translation>
val fallbackLocale: Locale
}
// etc...
// some example
@JsonClass(generateAdapter = true)
data class SomeComplexObject (
@EmbedJson
private val entity: Entity,
@EmbedJson
private val metas: InternalMetadata,
val someOtherStuffSpecificToThis: String
): Entity by entity, InternalMetadata by metas
// another example
@JsonClass(generateAdapter = true)
data class AnotherComplexObject (
@EmbedJson
private val entity: Entity,
@EmbedJson
private val metas: InternalMetadata,
@EmbedJson
private val i18n: I18N
): Entity by entity, InternalMetadata by metas, I18N by i18n
all these JSON are supposed to be flat, the usage is also gonna feel flat, I just want to be able to combine smaller pieces like this to more easily share code and part of the behavior.
It seems like the first example I gave you (the custom factory that links interfaces to implementations) is what you want? I don't see why that doesn't work for your case at least.
I don't have a different set of objects for each id, id are actually UUID autogenerated, that was an example.
i want moshi to parse my smaller objects (Entity, InternalMetadata, flat inside the bigger object).
I don't need polymorphic stuff. when i parse a SomeComplexObject i know i'm parsing that.
It's simply composition, AnotherComplexObject share stuff with SomeComplexObject but they are 2 different things.
{
"uuid": "cf85b6ec-4511-488d-a058-b2ca869eb21e",
"name": "This is a SomeComplexObject",
"description": "And is a composition of 2 smaller parts shared with other objects and some stuff which is specific to it",
"image": "https://......",
"content": {
"someOtherStuffSpecificToThis": "this is something only this kind of object have",
"created": "2019-11-30T07:51:29Z",
"lastUpdate": "2019-11-30T07:51:29Z",
}
````
and the other type:
{
"uuid": "b7f82470-295d-4203-ace7-89b197ce8506",
"name": "This is an AnotherComplexObject",
"description": "And is a composition of 3 smaller parts shared with other objects",
"image": "https://......",
"content": {
"en": { ... },
"it": { ... },
},
"fallbackLocale": "en",
"created": "2019-11-30T07:51:29Z",
"lastUpdate": "2019-11-30T07:51:29Z",
}
```
I don't need polymorphism, i just need a way to define some attributes of the json into smaller pieces and tell moshi to parse them embedded into the json.
My request is basically the same you can see here in Room database: https://developer.android.com/reference/android/arch/persistence/room/Embedded
just for Json / Moshi
@ZacSweers Sorry to ask here but this is the 'best' resource I have found and I've searched deep and wide...
So how to actually 'inline' (or embed) the object and use a codegen to help you do so? I think that was the original author's intent.
I am interested in writing something like this :
interface InnerFields{
val innerString1:String,
val innerString2:String
}
data class Inner(
val innerString1:String,
val innerString2:String
) :InnerFields
data class Outer(
val inlineThis:Inner,
val outerString1:String,
val outerString2:String
): InnerFields by inlineThis
Result (without values, to cut back on my own boilderplate example...):
{ "innerString1" : "..." , "innerString2" : "...", "outerString1" : " ...", "outerString2" : "..." }
Is it possible to write custom adapter that will 'know' that some objects should be flattened, without handling each 'parent' object separately, probably via using a JsonWriter.beginFlattern()? If it's any easier I only need a depth of one inner object ..
Most helpful comment
I don't have a different set of objects for each id, id are actually UUID autogenerated, that was an example.
i want moshi to parse my smaller objects (
Entity,InternalMetadata, flat inside the bigger object).I don't need polymorphic stuff. when i parse a
SomeComplexObjecti know i'm parsing that.It's simply composition,
AnotherComplexObjectshare stuff withSomeComplexObjectbut they are 2 different things.{
"uuid": "b7f82470-295d-4203-ace7-89b197ce8506",
"name": "This is an AnotherComplexObject",
"description": "And is a composition of 3 smaller parts shared with other objects",
"image": "https://......",
"content": {
"en": { ... },
"it": { ... },
},
"fallbackLocale": "en",
"created": "2019-11-30T07:51:29Z",
"lastUpdate": "2019-11-30T07:51:29Z",
}
```
I don't need polymorphism, i just need a way to define some attributes of the json into smaller pieces and tell moshi to parse them embedded into the json.