Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.6.8 livongo beplat172 #7

Open
wants to merge 19 commits into
base: 3.6-livongo
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@ jdk: openjdk8
scala:
- 2.10.7
- 2.11.12
- 2.12.8
- 2.13.0
- 2.12.11
- 2.13.2
matrix:
include:
- scala: 2.12.8
- scala: 2.12.11
jdk: openjdk11
sudo: false
cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt/launchers
- $HOME/.cache/coursier/v1
before_install:
- export TZ=Australia/Canberra
script:
- travis_retry sbt "++${TRAVIS_SCALA_VERSION}!" test
- travis_retry sbt "++${TRAVIS_SCALA_VERSION}!" mimaReportBinaryIssues test
before_cache:
- find $HOME/.sbt -name "*.lock" | xargs rm
- find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
# JSON4S [![Build Status](https://travis-ci.org/json4s/json4s.svg?branch=3.6)](https://travis-ci.org/json4s/json4s)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.json4s/json4s-core_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.json4s/json4s-core_2.12)

[![Join the chat at https://gitter.im/json4s/json4s](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/json4s/json4s?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

=======
At this moment there are at least 6 json libraries for scala, not counting the java json libraries.
All these libraries have a very similar AST. This project aims to provide a single AST to be used by other scala
json libraries.
Expand Down
2 changes: 1 addition & 1 deletion ast/src/main/scala/org/json4s/JsonAST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ object JsonAST {

case class JObject(obj: List[JField]) extends JValue {
type Values = Map[String, Any]
def values = obj.map { case (n, v) => (n, v.values) } toMap
def values = obj.iterator.map { case (n, v) => (n, v.values) }.toMap

override def equals(that: Any): Boolean = that match {
case o: JObject => obj.toSet == o.obj.toSet
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import xml.Group
import scala.xml.Group
import Dependencies._
import build._

Expand Down
15 changes: 15 additions & 0 deletions core/src/main/scala-2.13+/org/json4s/Compat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.json4s

import scala.language.reflectiveCalls

private[json4s] object Compat {
def makeCollection(clazz: Class[_], array: Array[_]): Option[Any] = {
reflect.ScalaSigReader.companions(clazz.getName).flatMap(_._2).map { c =>
val builder = c
.asInstanceOf[ {def newBuilder: collection.mutable.Builder[Any, Any]}]
.newBuilder
builder ++= array
builder.result
}
}
}
11 changes: 11 additions & 0 deletions core/src/main/scala-2.13-/org/json4s/Compat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.json4s

import scala.language.reflectiveCalls

private[json4s] object Compat {
def makeCollection(clazz: Class[_], array: Array[_]): Option[Any] = {
reflect.ScalaSigReader.companions(clazz.getName).flatMap(_._2).map {
_.asInstanceOf[ {def apply(elems: collection.Seq[_]): Any}].apply(array.toSeq)
}
}
}
57 changes: 18 additions & 39 deletions core/src/main/scala/org/json4s/Extraction.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,15 +328,21 @@ object Extraction {
else JString(ParserUtil.unquote(value.substring(1)))
}

def submap(prefix: String): Map[String, String] =
map.withFilter(t => t._1.startsWith(prefix)).map(
t => (t._1.substring(prefix.length), t._2)
)

val ArrayProp = new Regex("""^(\.([^\.\[]+))\[(\d+)\].*$""")
val ArrayElem = new Regex("""^(\[(\d+)\]).*$""")
val OtherProp = new Regex("""^(\.([^\.\[]+)).*$""")

def submap(prefix: String): Map[String, String] = {
map.withFilter { t =>
t._1 match {
case ArrayProp(p, _, _) if p == prefix => true
case ArrayElem(p, _) if p == prefix => true
case OtherProp(p, _) if p == prefix => true
case _ => false
}
} map { t => (t._1.substring(prefix.length), t._2) }
}

val uniquePaths = map.keys.foldLeft[Set[String]](Set()) {
(set, key) =>
key match {
Expand Down Expand Up @@ -456,36 +462,10 @@ object Extraction {
else if (tpe.erasure == classOf[java.util.ArrayList[_]]) mkCollection(a => new java.util.ArrayList[Any](a.toList.asJavaCollection))
else if (tpe.erasure.isArray) mkCollection(mkTypedArray)
else {
def getCompanion(className: String): Option[Any] = {
val c = try {
Some(Class.forName(className).isAssignableFrom(tpe.erasure))
} catch {
case _: ClassNotFoundException =>
None
}
c.flatMap { _ =>
reflect.ScalaSigReader.companions(tpe.erasure.getName).flatMap(_._2)
}
}

import language.reflectiveCalls

getCompanion("scala.collection.generic.GenericTraversableTemplate") match {
case Some(c) =>
val companion = c.asInstanceOf[{def apply(elems: collection.Seq[_]): Any}]
mkCollection(a => companion(a.toSeq))
case _ =>
getCompanion("scala.collection.Factory") match {
case Some(c) =>
val companion = c.asInstanceOf[{def newBuilder: collection.mutable.Builder[Any, Any]}]
mkCollection{ a =>
val b = companion.newBuilder
b ++= a
b.result
}
case _ =>
fail("Expected collection but got " + tpe)
}
mkCollection{ array =>
Compat.makeCollection(tpe.erasure, array).getOrElse(fail(
"Expected collection but got " + tpe
))
}
}
}
Expand Down Expand Up @@ -563,10 +543,9 @@ object Extraction {
else x
} catch {
case e @ MappingException(msg, _) =>
if (descr.isOptional &&
(!(formats.strictOptionParsing || formats.strictOptionParsingPre36) || extract(json, ScalaType[Null](implicitly)) == null))
if (descr.isOptional && (!(formats.strictOptionParsing || formats.strictOptionParsingPre36) || extract(json, ScalaType[Null](implicitly)) == null)) {
defv(None)
else fail("No usable value for " + descr.name + "\n" + msg, e)
} else fail("No usable value for " + descr.name + "\n" + msg, e)
}
}
}
Expand Down Expand Up @@ -769,4 +748,4 @@ object Extraction {
private[this] def formatDate(s: String, formats: Formats): Date = {
formats.dateFormat.parse(s).getOrElse(fail("Invalid date '" + s + "'"))
}
}
}
3 changes: 1 addition & 2 deletions core/src/main/scala/org/json4s/MonadicJValue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,7 @@ class MonadicJValue(jv: JValue) {
*/
def findField(p: JField => Boolean): Option[JField] = {
def find(json: JValue): Option[JField] = json match {
case JObject(fs) if fs exists p => fs find p
case JObject(fs) => fs.flatMap { case (_, v) => find(v) }.headOption
case JObject(fs) => fs.find(p).orElse(fs.flatMap { case (_, v) => find(v) }.headOption)
case JArray(l) => l.flatMap(find _).headOption
case _ => None
}
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/org/json4s/reflect/descriptors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ case class ClassDescriptor(
args.foldLeft(0)((s, arg) =>
if (names.contains(arg.name)) s+1
else if (arg.isOptional) s
else if (arg.hasDefault) s
else -100
)

Expand Down
14 changes: 7 additions & 7 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@ object Dependencies {
lazy val jaxbApi = "javax.xml.bind" % "jaxb-api" % "2.3.1" % "test"

lazy val jodaTime = Seq(
"joda-time" % "joda-time" % "2.10.1",
"org.joda" % "joda-convert" % "2.2.0"
"joda-time" % "joda-time" % "2.10.6",
"org.joda" % "joda-convert" % "2.2.1"
)
lazy val jackson = Seq(
"com.fasterxml.jackson.core" % "jackson-databind" % "2.9.8"
"com.fasterxml.jackson.core" % "jackson-databind" % "2.9.10.4"
)
lazy val scalaz_core = "org.scalaz" %% "scalaz-core" % "7.2.27"
lazy val scalaz_core = "org.scalaz" %% "scalaz-core" % "7.2.30"
lazy val paranamer = "com.thoughtworks.paranamer" % "paranamer" % "2.8"
lazy val specs = Def.setting{
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, v)) if v <= 10 =>
Seq("org.specs2" %% "specs2-scalacheck" % "3.10.0" % "test")
case _ =>
Seq("org.specs2" %% "specs2-scalacheck" % "4.5.1" % "test")
Seq("org.specs2" %% "specs2-scalacheck" % "4.9.4" % "test")
}
}
lazy val mockito = "org.mockito" % "mockito-core" % "2.24.5" % "test"
lazy val mockito = "org.mockito" % "mockito-core" % "3.3.3" % "test"

def scalaXml(scalaVersion: String) = {
PartialFunction.condOpt(CrossVersion.partialVersion(scalaVersion)){
case Some((2, scalaMajor)) if scalaMajor >= 13 =>
Seq("org.scala-lang.modules" %% "scala-xml" % "1.2.0")
Seq("org.scala-lang.modules" %% "scala-xml" % "1.3.0")
case Some((2, scalaMajor)) if scalaMajor >= 11 =>
Seq("org.scala-lang.modules" %% "scala-xml" % "1.1.0")
}.toList.flatten
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.2.8
sbt.version=1.3.10
5 changes: 3 additions & 2 deletions project/build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Keys._
import xml.Group
import MimaSettings.mimaSettings
import com.typesafe.tools.mima.plugin.MimaKeys.{mimaPreviousArtifacts, mimaReportBinaryIssues}

import livongo.build.project.{Artifactory, ArtifactoryPublisherPlugin}

object build {
Expand Down Expand Up @@ -83,13 +84,13 @@ object build {
Seq("-Ywarn-unused:imports")
}
},
version := "3.6.7-livongo-1.0.0",
version := "3.6.8-livongo-1.0.2-belplat-172",
javacOptions ++= Seq("-target", "1.8", "-source", "1.8"),
Seq(Compile, Test).map { scope =>
unmanagedSourceDirectories in scope += {
val base = (sourceDirectory in scope).value.getParentFile / Defaults.nameForSrc(scope.name)
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, v)) if v >= 13 && scalaVersion.value != "2.13.0-M3" =>
case Some((2, v)) if v >= 13 =>
base / s"scala-2.13+"
case _ =>
base / s"scala-2.13-"
Expand Down
7 changes: 4 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.6.4")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.5")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.1")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.2")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.0")
addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13")

scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")

Expand Down
25 changes: 25 additions & 0 deletions tests/src/test/scala/org/json4s/ExtractionBugs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -327,5 +327,30 @@ abstract class ExtractionBugs[T](mod: String) extends Specification with JsonMet
val json = Extraction.decompose(obj)
json mustEqual JObject("s" -> JString("hello"), "i" -> JInt(3))
}

"Extract error should preserve error message when strict option parsing is enabled" in {
implicit val formats = new DefaultFormats {
override val strictOptionParsing: Boolean = true
}

val obj = parse("""{"opt": "not an int"}""".stripMargin)

Extraction.extract[OptionOfInt](obj) must throwA(
new MappingException(
"""
|No usable value for opt
|Do not know how to convert JString(not an int) into int
|""".stripMargin.trim))
}

"Extract should succeed for optional field with null value" in {
val obj = parse("""{"opt":null}""".stripMargin)
Extraction.extract[OptionOfInt](obj) must_== OptionOfInt(None)
}

"Extract should succeed for missing optional field" in {
val obj = parse("""{}""".stripMargin)
Extraction.extract[OptionOfInt](obj) must_== OptionOfInt(None)
}
}
}
6 changes: 6 additions & 0 deletions tests/src/test/scala/org/json4s/ExtractionExamplesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ abstract class ExtractionExamples[T](mod: String, ser : json4s.Serialization) ex
Extraction.unflatten(m) must_== JObject(List(JField("foo", JArray(List(JInt(0), JInt(1), JInt(2))))))
}

"Unflatten example with field name is prefix of the other field name" in {
val m = Map(".data" -> "5", ".data_type" -> "6")

Extraction.unflatten(m) must_== JObject(JField("data", JInt(5)), JField("data_type", JInt(6)))
}

"Flatten and unflatten are symmetric" in {
val parsed = parse(testJson)

Expand Down
10 changes: 9 additions & 1 deletion tests/src/test/scala/org/json4s/SerializationSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,16 @@ abstract class SerializationSpec(serialization: Serialization, baseFormats: Form
val actual = Extraction.extract[AnotherModel](jackson.parseJson(json))
actual must_== expected
}
}

"#661 Matching algorithm picks least correct ctor" in {
serialization.read[BadSpec](s"""{"item2": 789, "item3": 123}""") must_== BadSpec(789, 123)
}
}
}
}

case class BadSpec(item2: Int, item3: Int, isVisited: Boolean = false)
case object BadSpec {
def apply(item1: Int, item2: Int, item3: Int): BadSpec = BadSpec(item2, item3)
}

Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ class MapSerializationExamples extends Specification {
"Map with Timestamp key" in {
val t2013 = new Timestamp(1356998400)
val t2014 = new Timestamp(1388534400)
// TODO use Map.apply instead of "new Map.Map2" when 2.13.0-M5 released
// https://github.com/scala/scala/commit/6a570b6f1f59222cae4f55aa25d48e3d4c22ea59
val pw: Map[Timestamp, String] = new Map.Map2(t2013, "hello", t2014, "world")
val pw: Map[Timestamp, String] = Map(t2013 -> "hello", t2014 -> "world")
val ser = swrite(pw)

val f2013 = formats.dateFormat.format(t2013)
Expand Down Expand Up @@ -80,9 +78,7 @@ class MapSerializationExamples extends Specification {
}

"case class with custom map" in {
// TODO use Map.apply instead of "new Map.Map2" when 2.13.0-M5 released
// https://github.com/scala/scala/commit/6a570b6f1f59222cae4f55aa25d48e3d4c22ea59
val pw = PlayerWithCustomMap("zortan", new Map.Map2("2013", "zortan13", "2014", "zortan14"))
val pw = PlayerWithCustomMap("zortan", Map("2013" -> "zortan13", "2014" -> "zortan14"))
val ser = swrite(pw)
val s: String = """{"name":"zortan","aliasByYear":{"2013":"zortan13","2014":"zortan14"}}"""
ser must_== s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,19 +170,15 @@ object SerializationExamples extends Specification {
}

"Generic Map with simple values example" in {
// TODO use Map.apply instead of "new Map.Map2" when 2.13.0-M5 released
// https://github.com/scala/scala/commit/6a570b6f1f59222cae4f55aa25d48e3d4c22ea59
val pw = PlayerWithGenericMap("zortan", new Map.Map2("1", "asd", "a", 3))
val pw = PlayerWithGenericMap("zortan", Map("1" -> "asd", "a" -> 3))
val ser = swrite(pw)
ser must_== """{"name":"zortan","infomap":{"1":"asd","a":3}}"""
read[PlayerWithGenericMap](ser) must_== pw
}

"Generic Map with case class and type hint example" in {
implicit val formats = native.Serialization.formats(ShortTypeHints(List(classOf[Player])))
// TODO use Map.apply instead of "new Map.Map3" when 2.13.0-M5 released
// https://github.com/scala/scala/commit/6a570b6f1f59222cae4f55aa25d48e3d4c22ea59
val pw = PlayerWithGenericMap("zortan", new Map.Map3("1", "asd", "a", 3, "friend", Player("joe")))
val pw = PlayerWithGenericMap("zortan", Map("1" -> "asd", "a" -> 3, "friend" -> Player("joe")))
val ser = swrite(pw)
ser must_== """{"name":"zortan","infomap":{"1":"asd","a":3,"friend":{"jsonClass":"Player","name":"joe"}}}"""
read[PlayerWithGenericMap](ser) must_== pw
Expand Down
1 change: 1 addition & 0 deletions version.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version in ThisBuild := "3.6.8"