Ever wanted to just build a nice json in kotlin, b...
# feed
b
Ever wanted to just build a nice json in kotlin, but found raw string templates hard to keep pretty? Well turns out you can make full json DSL with just a few lines of code.
๐Ÿ’ก 2
K 3
๐Ÿ‘Œ 10
mind blown 3
๐Ÿ‘ 1
๐Ÿ˜ฎ 1
๐Ÿ†’ 1
Serializes to this
Copy code
{
  "boolean": true,
  "number": 1,
  "string": "str",
  "null": null,
  "array": [
    1,
    "str",
    null,
    {},
    [
      1,
      "2",
      null,
      3
    ]
  ],
  "object": {
    "boolean": true,
    "number": 1,
    "string": "str",
    "null": null,
    "array": [
      1,
      "str",
      null
    ]
  }
}
r
Serializes with what? kotlinx.serialization?
b
With whatever you choose. This just builds a map representation of json.
i.e. It doesn't depend on any serialization voodoo to work.
But for transparency, I ran the generated map through Jackson to get that json output.
โž• 1
a
You can also use the "inject language" feature in the IDE
๐Ÿ‘ 4
b
I'm aware, but I found that hard to maintain in terms of formatting. Plus it adds lots of
$
that just feels unnatural. I found the DSL much more comfortable to work with as it closer matches json structure and allows direct references to variables (no
$
required)
j
kotlin serialization had something similar to this no? but they changed to
build...
functions
b
Did they? I'm not aware of that. Got a link?
j
I can't found one, it was removed a lot of months ago
by
can't be replaced by
operator fun equals
?
b
Hmm, good idea! Or +=
Not sure which is better
j
personally when I create manually a Json I always fail and I use = instead of :
that is the reason I thought in = hahah
any way, are you going to publish it as KMP library? not a lot of code, but it can be used in a lot of projects so avoid copypasting can be great
t
something interesting I saw in a DSL for JSON objects once: instead of the
by
infix function, you could implement a
rangeTo
operator function, so
Copy code
{
    "number" by 1
}
becomes
Copy code
{
    "number"..1
}
which looks a bit like the
:
just fell over ๐Ÿ˜„
๐Ÿ˜‚ 2
@Javier
equals
already exists on every type and extension functions can't override member functions. Also, it would result in the usage of
==
, not
=
and wouldn't work properly with null values (if I remember the behaviour correctly)
j
i like operator %, it has two dots also https://gist.github.com/jnorthrup/51f2b23bde522bb03d74aa552c295f6b it feels like there are some wicked delegation opportunities
j
ah, not sure if
=
can be used or only
==
t
=
assigns a value, the equality check is always
==
j
yeah, but there is no another operator for it no?
maybe
to
to keep consistence with languages features if
=
cant be used
๐Ÿ‘† 2
j
Copy code
infix operator fun String.rem(value: Any?) = this `โš` value //two escaped dots if inlined
    
    @JSSetterDsl
    /**
     * set primitive
     */
    infix fun String.`โš`(t: Any?) { map[this] = t }  
...
val `{}` by lazy { JSObj().apply(fun JSObj.() = Unit) }
some options. alternatives to backticks would be great in kotlin.
b
Backticks are a big nono for mpp projects thanks to js ๐Ÿ˜€
j
and they are hard to write, sadly
b
Ok well, I went ahead and published that as an MPP lib ๐Ÿ˜„ It even builds json strings for you! kon@1.0.0 is out! All proposed setters are supported with the exception of
==
and backticks.
K 1
๐Ÿ‘ 1
๐Ÿ‘ 4
๐Ÿ†’ 1
j
Copy code
Modules
kon - wrapper module
this link is broken
b
What do you mean?
Nvm, figured it out and fixed it
o
I like the idea but I'm worried about the operator overkill: Code written by multiple persons can become hard to read if one person would use
by
, the second adds stuff with
%
, and so on. I'd stick with the usual
to
as this aligns with Kotlin map notation, and avoid synonymous operators.
๐Ÿ‘ 2
b
I had same thoughts. I'll use it in my own projects for the next week and will decide on what feels most natural. The rest will be removed. Also I'm a bit iffy about using to as there's an infix function like that in stdlib already used to build pairs. Shadowing might be an issue
j
I think
to
is enough and it is used to build maps, I like += too.
b
How about just +? It's the same hey on the keyboard
Although it doesn't convey the intent as well...
j
+ for standalone values is fine, but in json there are not standalone values, semantically looks strange
e
did anybody bring up the json builders in kotlinx.serialization? they're a bit more verbose but they do exist (buildJsonObject, buildJsonArray, etc.)
b
@Javier did but couldn't get a link ๐Ÿ˜„ Also to get them you have to bring in entire serialization lib (heavy), which might not always be what you want.
j
Maybe you can find the commit where they moved to
build...
Another cool thing you can add to your lib, is compatibility with Kotlin Serialization
so you generate a
JsonElement
with your syntax
b
I'm thinking of adding that as a new module. So ppl could still use it without serialization
e
o
@ephemient Thanks for bringing that up. Some thoughts: The
kotlinx.serialization
API seems to ease onboarding (with IDE support, you'd just type
put
and use completion). I am undecided whether its verbosity might increase reading/scanning time when compared to a map-like
to
notation. Probably not much, if at all. In the end, I tend to think it is like using different indentation styles: While I do prefer certain styles over others, what really matters most is consistency.
b
Apologies for the spam, but kon@1.1.0 is out with standardised setters and optional kontinx-serialization-json support for two-way conversions between
KON
and
JsonObject
๐Ÿ‘ 4
m
yeah this is better! When I saw all those different operators to perform the same action i was very confused
e
"array"[1]
in KObject may be an issueโ€ฆ what if you changed the DSL to
Copy code
kobj {
  "scalar" to 1
  "object".to { ... }
  "array".to[ ... ]
}
instead? seems more consistent to me
b
Why is String[] an issue? That extension is only available in kobject scope and hooks onto string class
Or do you mean string char accessor overlap?
I'll do some testing to see how relevant it actually is
e
yes, it'll resolve to String.get over your extension