Skip to content

Commit

Permalink
Added codecs for java.time classes and cleaned up codec setup logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Marc Knaup committed Nov 4, 2017
1 parent a306a10 commit 1c5b40e
Show file tree
Hide file tree
Showing 101 changed files with 739 additions and 250 deletions.
11 changes: 1 addition & 10 deletions Examples/0030-TypeEncoderCodecs.kt
Expand Up @@ -2,7 +2,6 @@ package examples

import com.github.fluidsonic.fluid.json.*
import java.time.Instant
import java.time.format.DateTimeFormatter


object EncodingExample {
Expand All @@ -12,7 +11,7 @@ object EncodingExample {
// Using a codec for encoding specific Kotlin types simplifies JSON serialization a lot

val serializer = JSONSerializer.builder()
.encodingWith(EventCodec, InstantCodec)
.encodingWith(EventCodec)
.build()

val json = serializer.serializeValue(listOf(
Expand Down Expand Up @@ -45,12 +44,4 @@ object EncodingExample {
}
}
}


private object InstantCodec : AbstractJSONEncoderCodec<Instant, JSONCoderContext>() {

override fun encode(value: Instant, encoder: JSONEncoder<JSONCoderContext>) {
encoder.writeString(DateTimeFormatter.ISO_INSTANT.format(value))
}
}
}
17 changes: 1 addition & 16 deletions Examples/0031-TypeDecoderCodecs.kt
Expand Up @@ -2,7 +2,6 @@ package examples

import com.github.fluidsonic.fluid.json.*
import java.time.Instant
import java.time.format.DateTimeParseException


object DecodingExample {
Expand All @@ -12,7 +11,7 @@ object DecodingExample {
// Using a codec for decoding specific classes simplifies JSON parsing a lot

val parser = JSONParser.builder()
.decodingWith(EventCodec, InstantCodec)
.decodingWith(EventCodec)
.build()

val json = parser.parseValueOfType<List<Event>>("""
Expand Down Expand Up @@ -83,18 +82,4 @@ object DecodingExample {
)
}
}


private object InstantCodec : AbstractJSONDecoderCodec<Instant, JSONCoderContext>() {

override fun decode(valueType: JSONCodableType<in Instant>, decoder: JSONDecoder<JSONCoderContext>): Instant =
decoder.readString().let {
try {
Instant.parse(it)
}
catch (e: DateTimeParseException) {
throw JSONException("Cannot parse Instant '$it'", e)
}
}
}
}
25 changes: 2 additions & 23 deletions Examples/0032-TypeCodecs.kt
Expand Up @@ -2,8 +2,6 @@ package examples

import com.github.fluidsonic.fluid.json.*
import java.time.Instant
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException


object CodingExample {
Expand All @@ -22,13 +20,13 @@ object CodingExample {
)

val serializer = JSONSerializer.builder()
.encodingWith(EventCodec, InstantCodec)
.encodingWith(EventCodec)
.build()

val json = serializer.serializeValue(input)

val parser = JSONParser.builder()
.decodingWith(EventCodec, InstantCodec)
.decodingWith(EventCodec)
.build()

val output = parser.parseValueOfType<List<Event>>(json)
Expand Down Expand Up @@ -78,23 +76,4 @@ object CodingExample {
}
}
}


private object InstantCodec : AbstractJSONCodec<Instant, JSONCoderContext>() {

override fun decode(valueType: JSONCodableType<in Instant>, decoder: JSONDecoder<JSONCoderContext>): Instant =
decoder.readString().let {
try {
Instant.parse(it)
}
catch (e: DateTimeParseException) {
throw JSONException("Cannot parse Instant '$it'", e)
}
}


override fun encode(value: Instant, encoder: JSONEncoder<JSONCoderContext>) {
encoder.writeString(DateTimeFormatter.ISO_INSTANT.format(value))
}
}
}
25 changes: 2 additions & 23 deletions Examples/0033-CodingAsStream.kt
Expand Up @@ -3,8 +3,6 @@ package examples
import com.github.fluidsonic.fluid.json.*
import java.io.StringWriter
import java.time.Instant
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException


object CodingAsStreamExample {
Expand Down Expand Up @@ -34,7 +32,7 @@ object CodingAsStreamExample {

private fun decode(json: String) =
JSONDecoder.builder()
.codecs(EventCodec, InstantCodec)
.codecs(EventCodec)
.source(json)
.build()
.use { decoder ->
Expand All @@ -54,7 +52,7 @@ object CodingAsStreamExample {
val writer = StringWriter()

return JSONEncoder.builder()
.codecs(EventCodec, InstantCodec)
.codecs(EventCodec)
.destination(writer)
.build()
.use { encoder ->
Expand Down Expand Up @@ -108,23 +106,4 @@ object CodingAsStreamExample {
}
}
}


private object InstantCodec : AbstractJSONCodec<Instant, JSONCoderContext>() {

override fun decode(valueType: JSONCodableType<in Instant>, decoder: JSONDecoder<JSONCoderContext>): Instant =
decoder.readString().let {
try {
Instant.parse(it)
}
catch (e: DateTimeParseException) {
throw JSONException("Cannot parse Instant '$it'", e)
}
}


override fun encode(value: Instant, encoder: JSONEncoder<JSONCoderContext>) {
encoder.writeString(DateTimeFormatter.ISO_INSTANT.format(value))
}
}
}
25 changes: 24 additions & 1 deletion README.md
Expand Up @@ -324,6 +324,29 @@ The default implementations of `JSONReader` and `JSONParser` decode JSON types a
| `object<string,*>` | `Map<String,*>` |
| `string` | `String` |

### Extended Types

The following classes of the `java.time` package can also be decoded and encoded out of the box:

| Kotlin | JSON | Remarks
| ---------------- | -------- | -------
| `DayOfWeek` | `string` | `"monday"`, …, `"friday"`
| `Duration` | `string` | using `.parse()` / `.toString()`
| `Instant` | `string` | using `.parse()` / `.toString()`
| `LocalDate` | `string` | using `.parse()` / `.toString()`
| `LocalDateTime` | `string` | using `.parse()` / `.toString()`
| `LocalTime` | `string` | using `.parse()` / `.toString()`
| `MonthDay` | `string` | using `.parse()` / `.toString()`
| `Month` | `string` | `"january"`, …, `"december"`
| `OffsetDateTime` | `string` | using `.parse()` / `.toString()`
| `OffsetTime` | `string` | using `.parse()` / `.toString()`
| `Period` | `string` | using `.parse()` / `.toString()`
| `Year` | `int` | using `.value`
| `YearMonth` | `string` | using `.parse()` / `.toString()`
| `ZonedDateTime` | `string` | using `.parse()` / `.toString()`
| `ZoneId` | `string` | using `.of()` / `.id`
| `ZoneOffset` | `string` | using `.of()` / `.id`



Architecture
Expand Down Expand Up @@ -371,6 +394,7 @@ parser/serializer.
| `AbstractJSONCodec` | Abstract base class which simplifies implementing `JSONCodec`.
| `AbstractJSONDecoderCodec` | Abstract base class which simplifies implementing `JSONDecoderCodec`.
| `AbstractJSONEncoderCodec` | Abstract base class which simplifies implementing `JSONEncoderCodec`.
| `DefaultJSONCodecs` | Contains lists of default codecs which can be used when contructing custom `JSONCodecProvider`s.
| `JSONCodableType` | Roughly describes a Kotlin type which can be decoded from JSON. It includes relevant generic information which allows decoding for example `List<Something>` instead of just `List<*>`. Also known as [type token](http://gafter.blogspot.de/2006/12/super-type-tokens.html)).
| `JSONCodec` | Interface for classes which implement both, `JSONEncoderCodec` and `JSONDecoderCodec`. Also simplifies creating such codecs.
| `JSONCodecProvider` | Interface for classes which when given a `JSONCodableType` (for decoding) or `KClass` (for encoding) return a codec which is able to decode/encode values of that type.
Expand All @@ -395,7 +419,6 @@ Future Planning
This is on the backlog for later consideration, in no specific order:

- [Add KDoc to all public API](https://github.com/fluidsonic/fluid-json/issues/28)
- [Add extended set of standard codecs (e.g. for `java.time`)](https://github.com/fluidsonic/fluid-json/issues/17)
- [Add annotation-based preprocessor for automatic create codecs](https://github.com/fluidsonic/fluid-json/issues/16)
- [Add performance testing](https://github.com/fluidsonic/fluid-json/issues/4)
- [Add low-level support for `BigDecimal` / `BigInteger`](https://github.com/fluidsonic/fluid-json/issues/18)
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
59 changes: 59 additions & 0 deletions Sources/Codecs/DefaultJSONCodecs.kt
@@ -0,0 +1,59 @@
package com.github.fluidsonic.fluid.json


object DefaultJSONCodecs {

val basic = listOf(
AnyJSONDecoderCodec,
ArrayJSONCodec,
BooleanArrayJSONCodec,
BooleanJSONCodec,
ByteArrayJSONCodec,
ByteJSONCodec,
DoubleArrayJSONCodec,
DoubleJSONCodec,
FloatArrayJSONCodec,
FloatJSONCodec,
IntArrayJSONCodec,
IntJSONCodec,
ListJSONDecoderCodec,
LongArrayJSONCodec,
LongJSONCodec,
MapJSONCodec,
SequenceJSONCodec,
ShortArrayJSONCodec,
ShortJSONCodec,
StringJSONCodec,
IterableJSONEncoderCodec, // after subclasses
NumberJSONCodec // after subclasses
)


val extended = listOf(
DayOfWeekJSONCodec,
DurationJSONCodec,
InstantJSONCodec,
LocalDateJSONCodec,
LocalDateTimeJSONCodec,
LocalTimeJSONCodec,
MonthDayJSONCodec,
MonthJSONCodec,
OffsetTimeJSONCodec,
OffsetTimeJSONCodec,
PeriodJSONCodec,
YearJSONCodec,
YearMonthJSONCodec,
ZonedDateTimeJSONCodec,
ZoneIdJSONCodec,
ZoneOffsetJSONCodec
)


val nonRecursive = listOf(
ArrayJSONCodec.nonRecursive,
ListJSONDecoderCodec.nonRecursive,
MapJSONCodec.nonRecursive,
SequenceJSONCodec.nonRecursive,
IterableJSONEncoderCodec.nonRecursive // after subclasses
)
}
34 changes: 34 additions & 0 deletions Sources/Codecs/Extended/DayOfWeekJSONCodec.kt
@@ -0,0 +1,34 @@
package com.github.fluidsonic.fluid.json

import java.time.DayOfWeek


// TODO use Enum codec once implemented
object DayOfWeekJSONCodec : AbstractJSONCodec<DayOfWeek, JSONCoderContext>() {

override fun decode(valueType: JSONCodableType<in DayOfWeek>, decoder: JSONDecoder<JSONCoderContext>) =
decoder.readString().let { raw ->
when (raw) {
"monday" -> DayOfWeek.MONDAY
"tuesday" -> DayOfWeek.TUESDAY
"wednesday" -> DayOfWeek.WEDNESDAY
"thursday" -> DayOfWeek.THURSDAY
"friday" -> DayOfWeek.FRIDAY
"saturday" -> DayOfWeek.SATURDAY
"sunday" -> DayOfWeek.SUNDAY
else -> throw JSONException("Invalid DayOfWeek value: $raw")
}
}


override fun encode(value: DayOfWeek, encoder: JSONEncoder<JSONCoderContext>) =
encoder.writeString(when (value) {
DayOfWeek.MONDAY -> "monday"
DayOfWeek.TUESDAY -> "tuesday"
DayOfWeek.WEDNESDAY -> "wednesday"
DayOfWeek.THURSDAY -> "thursday"
DayOfWeek.FRIDAY -> "friday"
DayOfWeek.SATURDAY -> "saturday"
DayOfWeek.SUNDAY -> "sunday"
})
}
22 changes: 22 additions & 0 deletions Sources/Codecs/Extended/DurationJSONCodec.kt
@@ -0,0 +1,22 @@
package com.github.fluidsonic.fluid.json

import java.time.DateTimeException
import java.time.Duration


object DurationJSONCodec : AbstractJSONCodec<Duration, JSONCoderContext>() {

override fun decode(valueType: JSONCodableType<in Duration>, decoder: JSONDecoder<JSONCoderContext>) =
decoder.readString().let { raw ->
try {
Duration.parse(raw)!!
}
catch (e: DateTimeException) {
throw JSONException("Cannot parse Duration value: $raw")
}
}


override fun encode(value: Duration, encoder: JSONEncoder<JSONCoderContext>) =
encoder.writeString(value.toString())
}
22 changes: 22 additions & 0 deletions Sources/Codecs/Extended/InstantJSONCodec.kt
@@ -0,0 +1,22 @@
package com.github.fluidsonic.fluid.json

import java.time.DateTimeException
import java.time.Instant


object InstantJSONCodec : AbstractJSONCodec<Instant, JSONCoderContext>() {

override fun decode(valueType: JSONCodableType<in Instant>, decoder: JSONDecoder<JSONCoderContext>) =
decoder.readString().let { raw ->
try {
Instant.parse(raw)!!
}
catch (e: DateTimeException) {
throw JSONException("Cannot parse Instant value: $raw")
}
}


override fun encode(value: Instant, encoder: JSONEncoder<JSONCoderContext>) =
encoder.writeString(value.toString())
}
22 changes: 22 additions & 0 deletions Sources/Codecs/Extended/LocalDateJSONCodec.kt
@@ -0,0 +1,22 @@
package com.github.fluidsonic.fluid.json

import java.time.DateTimeException
import java.time.LocalDate


object LocalDateJSONCodec : AbstractJSONCodec<LocalDate, JSONCoderContext>() {

override fun decode(valueType: JSONCodableType<in LocalDate>, decoder: JSONDecoder<JSONCoderContext>) =
decoder.readString().let { raw ->
try {
LocalDate.parse(raw)!!
}
catch (e: DateTimeException) {
throw JSONException("Cannot parse LocalDate value: $raw")
}
}


override fun encode(value: LocalDate, encoder: JSONEncoder<JSONCoderContext>) =
encoder.writeString(value.toString())
}
22 changes: 22 additions & 0 deletions Sources/Codecs/Extended/LocalDateTimeJSONCodec.kt
@@ -0,0 +1,22 @@
package com.github.fluidsonic.fluid.json

import java.time.DateTimeException
import java.time.LocalDateTime


object LocalDateTimeJSONCodec : AbstractJSONCodec<LocalDateTime, JSONCoderContext>() {

override fun decode(valueType: JSONCodableType<in LocalDateTime>, decoder: JSONDecoder<JSONCoderContext>) =
decoder.readString().let { raw ->
try {
LocalDateTime.parse(raw)!!
}
catch (e: DateTimeException) {
throw JSONException("Cannot parse LocalDateTime value: $raw")
}
}


override fun encode(value: LocalDateTime, encoder: JSONEncoder<JSONCoderContext>) =
encoder.writeString(value.toString())
}

0 comments on commit 1c5b40e

Please sign in to comment.