-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update dependencies, add Scala Native support for circe integration, …
…add jsoniter-scala integration
- Loading branch information
1 parent
71f93a6
commit 53beacb
Showing
5 changed files
with
263 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
jsoniter-scala/src/main/scala/com/free2move/geoscala/jsoniter_scala.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* | ||
* Copyright 2019 GHM Mobile Development GmbH | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.free2move.geoscala | ||
|
||
import com.github.plokhotnyuk.jsoniter_scala.core._ | ||
import com.github.plokhotnyuk.jsoniter_scala.macros._ | ||
|
||
object jsoniter_scala { | ||
// Uncomment for printing of generating codecs | ||
// implicit val printCodec: CodecMakerConfig.PrintCodec = new CodecMakerConfig.PrintCodec {} | ||
|
||
implicit val coordinateCodec: JsonValueCodec[Coordinate] = | ||
new JsonValueCodec[Coordinate] { | ||
override def decodeValue(in: JsonReader, default: Coordinate): Coordinate = | ||
if (in.isNextToken('[')) { | ||
val lon = in.readDouble() | ||
if (!in.isNextToken(',')) in.commaError() | ||
val lat = in.readDouble() | ||
while (in.isNextToken(',')) in.skip() | ||
if (!in.isCurrentToken(']')) in.arrayEndOrCommaError() | ||
Coordinate(lon, lat) | ||
} else in.readNullOrTokenError(default, '[') | ||
|
||
override def encodeValue(x: Coordinate, out: JsonWriter): Unit = { | ||
out.writeArrayStart() | ||
out.writeVal(x.longitude) | ||
out.writeVal(x.latitude) | ||
out.writeArrayEnd() | ||
} | ||
|
||
override def nullValue: Coordinate = null | ||
} | ||
|
||
implicit val listOfCoordinatesCodec: JsonValueCodec[List[Coordinate]] = | ||
JsonCodecMaker.make | ||
|
||
implicit val listOfListOfCoordinatesCodec: JsonValueCodec[List[List[Coordinate]]] = | ||
JsonCodecMaker.make | ||
|
||
implicit val listOfListOfListOfCoordinatesCodec: JsonValueCodec[List[List[List[Coordinate]]]] = | ||
JsonCodecMaker.make | ||
|
||
implicit val pointCodec: JsonValueCodec[Point] = | ||
makeGeometryCodec("Point", _.coordinates, Point) | ||
|
||
implicit val multiPointCodec: JsonValueCodec[MultiPoint] = | ||
makeGeometryCodec("MultiPoint", _.coordinates, MultiPoint) | ||
|
||
implicit val lineStringCodec: JsonValueCodec[LineString] = | ||
makeGeometryCodec("LineString", _.coordinates, LineString) | ||
|
||
implicit val multiLineStringCodec: JsonValueCodec[MultiLineString] = | ||
makeGeometryCodec("MultiLineString", _.coordinates, MultiLineString) | ||
|
||
implicit val polygonCodec: JsonValueCodec[Polygon] = | ||
makeGeometryCodec("Polygon", _.coordinates, Polygon) | ||
|
||
implicit val multiPolygonCodec: JsonValueCodec[MultiPolygon] = | ||
makeGeometryCodec("MultiPolygon", _.coordinates, MultiPolygon) | ||
|
||
private def makeGeometryCodec[C: JsonValueCodec, G <: Geometry](`type`: String, coords: G => C, geom: C => G): JsonValueCodec[G] = | ||
new JsonValueCodec[G] { | ||
private val coordinatesCodec: JsonValueCodec[C] = implicitly[JsonValueCodec[C]] | ||
|
||
override def decodeValue(in: JsonReader, default: G): G = | ||
if (in.isNextToken('{')) { | ||
var coordinates: C = coordinatesCodec.nullValue | ||
var mask = 3 | ||
var len = -1 | ||
while (len < 0 || in.isNextToken(',')) { | ||
len = in.readKeyAsCharBuf() | ||
if (in.isCharBufEqualsTo(len, "type")) { | ||
if ((mask & 0x1) != 0) mask ^= 0x1 | ||
else in.duplicatedKeyError(len) | ||
len = in.readStringAsCharBuf() | ||
if (!in.isCharBufEqualsTo(len, `type`)) in.discriminatorValueError("type") | ||
} else if (in.isCharBufEqualsTo(len, "coordinates")) { | ||
if ((mask & 0x2) != 0) mask ^= 0x2 | ||
else in.duplicatedKeyError(len) | ||
coordinates = coordinatesCodec.decodeValue(in, coordinates) | ||
} else in.skip() | ||
} | ||
geom(coordinates) | ||
} else in.readNullOrTokenError(default, '}') | ||
|
||
override def encodeValue(x: G, out: JsonWriter): Unit = { | ||
out.writeObjectStart() | ||
out.writeNonEscapedAsciiKey("type") | ||
out.writeNonEscapedAsciiVal(`type`) | ||
out.writeNonEscapedAsciiKey("coordinates") | ||
coordinatesCodec.encodeValue(coords(x), out) | ||
out.writeObjectEnd() | ||
} | ||
|
||
override def nullValue: G = null.asInstanceOf[G] | ||
} | ||
|
||
implicit val geometryCodec: JsonValueCodec[Geometry] = | ||
JsonCodecMaker.make | ||
|
||
implicit def featureCodec[P: JsonValueCodec]: JsonValueCodec[Feature[P]] = | ||
JsonCodecMaker.make | ||
|
||
implicit def featureCollectionCodec[P: JsonValueCodec]: JsonValueCodec[FeatureCollection[P]] = | ||
JsonCodecMaker.make | ||
|
||
implicit def geoJson[P: JsonValueCodec]: JsonValueCodec[GeoJson[P]] = | ||
new JsonValueCodec[GeoJson[P]] { | ||
private val fc: JsonValueCodec[Feature[P]] = featureCodec | ||
private val fcc: JsonValueCodec[FeatureCollection[P]] = featureCollectionCodec | ||
|
||
override def decodeValue(in: JsonReader, default: GeoJson[P]): GeoJson[P] = { | ||
in.setMark() | ||
if (in.isNextToken('{')) { | ||
in.skipToKey("type") | ||
val len = in.readStringAsCharBuf() | ||
in.rollbackToMark() | ||
if (in.isCharBufEqualsTo(len, "Feature")) fc.decodeValue(in, fc.nullValue) | ||
else if (in.isCharBufEqualsTo(len, "FeatureCollection")) fcc.decodeValue(in, fcc.nullValue) | ||
else geometryCodec.decodeValue(in, geometryCodec.nullValue).asInstanceOf[GeoJson[P]] | ||
} else in.readNullOrTokenError(default, '{') | ||
} | ||
|
||
override def encodeValue(x: GeoJson[P], out: JsonWriter): Unit = | ||
x match { | ||
case f: Feature[P] => fc.encodeValue(f, out) | ||
case fc: FeatureCollection[P] => fcc.encodeValue(fc, out) | ||
case _ => geometryCodec.encodeValue(x.asInstanceOf[Geometry], out) | ||
} | ||
|
||
override def nullValue: GeoJson[P] = null.asInstanceOf[GeoJson[P]] | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
jsoniter-scala/src/test/scala/com/free2move/geoscala/JsoniterScalaDecodingTests.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* Copyright 2019 GHM Mobile Development GmbH | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.free2move.geoscala | ||
|
||
import com.github.plokhotnyuk.jsoniter_scala.core._ | ||
import com.github.plokhotnyuk.jsoniter_scala.macros._ | ||
import org.scalatest.EitherValues | ||
import org.scalatest.flatspec.AnyFlatSpec | ||
import org.scalatest.matchers.should.Matchers | ||
|
||
class JsoniterScalaDecodingTests extends AnyFlatSpec with Matchers with EitherValues { | ||
|
||
import com.free2move.geoscala.jsoniter_scala._ | ||
|
||
"The jsoniter-scala codecs" should "handle simple 2D points" in { | ||
val json = | ||
"""{ | ||
"type": "Point", | ||
"coordinates": [ | ||
12.3046875, | ||
51.8357775 | ||
] | ||
}""" | ||
readFromString[Point](json) shouldBe Point(Coordinate(12.3046875, 51.8357775)) | ||
readFromString[Geometry](json) shouldBe Point(Coordinate(12.3046875, 51.8357775)) | ||
} | ||
|
||
it should "handle points with more dimensions" in { | ||
val json = | ||
"""{ | ||
"type": "Point", | ||
"coordinates": [ | ||
12.3046875, | ||
51.8357775, | ||
7.000, | ||
42.12345 | ||
] | ||
}""" | ||
readFromString[Point](json) shouldBe Point(Coordinate(12.3046875, 51.8357775)) | ||
readFromString[Geometry](json) shouldBe Point(Coordinate(12.3046875, 51.8357775)) | ||
} | ||
|
||
it should "handle FeatureCollection without Properties as pure JSON correctly" in { | ||
type Json = Map[String, Int] | ||
|
||
implicit val jsonCodec: JsonValueCodec[Json] = JsonCodecMaker.make | ||
|
||
val json = | ||
"""{ | ||
"type": "FeatureCollection", | ||
"features": [ | ||
{ | ||
"type": "Feature", | ||
"properties": { | ||
"id": 7 | ||
}, | ||
"geometry": { | ||
"type": "Point", | ||
"coordinates": [ | ||
12.3046875, | ||
51.8357775 | ||
] | ||
} | ||
} | ||
] | ||
}""" | ||
readFromString[FeatureCollection[Json]](json) shouldBe FeatureCollection( | ||
List(Feature(Map("id" -> 7), Point(Coordinate(12.3046875, 51.8357775)))) | ||
) | ||
readFromString[GeoJson[Json]](json) shouldBe FeatureCollection( | ||
List(Feature(Map("id" -> 7), Point(Coordinate(12.3046875, 51.8357775)))) | ||
) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.4.13") | ||
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.1") | ||
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.5") | ||
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.11.0") | ||
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") | ||
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.7") | ||
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.2.0") |