ScalaJSON library, currently provides two minimal implementations of JSON AST, one that is designed for typical use and another that is designed for performance/corner cases.
Built for Scala 2.10.x, 2.11.x, 2.12.x, and 2.13.x milestones.
"org.scala-lang.platform" %% "scalajson" % "1.0.0-M4"
If you are using Scala.js, you need to do
"org.scala-lang.platform" %%% "scalajson" % "1.0.0-M4"
Implementation is in scalajson.ast.JValue
- Fully immutable (all underlying collections/types used are immutable)
constant
/effective constant
lookup time forscalajson.ast.JArray
/scalajson.ast.JObject
- Adherence to the typical use for the JSON standard.
- Number representation for
scalajson.ast.JNumber
is aString
which checks if its a valid JSON representation of a number (http://stackoverflow.com/a/13502497/1519631)- Equals will properly detect if two numbers are equal, i.e.
scalajson.ast.JNumber("34.00") == scalajson.ast.JNumber("34")
- Hashcode has been designed to provide consistent hash for numbers of unlimited precision.
- If you construct a JNumber with
Float.NaN
/Float.PositiveInfinity
/Float.NegativeInfinity
/Double.NaN
/Double.PositiveInfinity
/Double.NegativeInfinity
it will return aJNull
- You can construct an unlimited precision number using a string, i.e.
JNumber("34324")
. Returns anOption[JNumber]
(will returnNone
ifString
isn't a valid number)- Note that this doesn't work for Scala 2.10 due to a restriction with how private constructor case classes are handled. For this reason a
JNumber.fromString
method is provided which compiles on all platforms and scala versions
- Note that this doesn't work for Scala 2.10 due to a restriction with how private constructor case classes are handled. For this reason a
- Equals will properly detect if two numbers are equal, i.e.
scalajson.ast.JObject
is an actualMap[String,JValue]
. This means that it doesn't handle duplicate keys for ascalajson.ast.JObject
, nor does it handle key ordering.scalajson.ast.JArray
is anVector
.
- Number representation for
- Library does not allow invalid JSON in the representation and hence we can guarantee that a
scalajson.ast.JValue
will always contain a valid structure that can be serialized/rendered into JSON.- Note that you can lose precision when using
scalajson.ast.JNumber
inScala.js
(seeScala.js
section for more info). - The
.copy
method ofscalajson.ast.JNumber
has been overridden to make sure you can't replace the internalString
with an incorrect number- Will throw a
NumberFormatException
if you use.copy
with an invalid JSON number
- Will throw a
- Note that you can lose precision when using
- Due to the above, has properly implemented deep equality for all types of
scalajson.ast.JValue
Implementation is in scalajson.unsafe.JValue
- Uses the best performing datastructure's for high performance/low memory usage in construction of a
unsafe.JValue
scalajson.ast.unsafe.JArray
stored as anArray
for JVM andjs.Array
for Scala.jsscalajson.ast.unsafe.JObject
stored as anArray
for JVM andjs.Array
for Scala.jsscalajson.ast.unsafe.JNumber
stored as aString
- Doesn't use
Scala
'sstdlib
collection's library - Defer all runtime errors. We don't throw errors if you happen to provide Infinity/NaN or other invalid data.
- Allow duplicate and ordered keys for a
scalajson.ast.unsafe.JObject
. - Allow any length/precision of numbers for
scalajson.ast.unsafe.JNumber
since its represented as aString
- Equals/hashcode only checks for
String
equality, not number equality.- Means that
scalajson.ast.unsafe.JNumber("34.00")
is not equal toscalajson.ast.unsafe.JNumber("34")
- Means that
- Equals/hashcode only checks for
- This means that
scalajson.ast.unsafe.JValue
can represent everything that can can be considered valid under the official JSON spec, even if its not considered sane (i.e. duplicate keys for ascalajson.ast.unsafe.JObject
).- Also means it can hold invalid data, due to not doing runtime checks
- Is referentially transparent in regards to
String
->scalajson.ast.unsafe.JValue
->String
sincescalajson.ast.unsafe.JObject
preserves ordering/duplicate keys - Implements structural equality for both
hashCode
andequals
. If you need reference equality you can useeq
and if you need referencehashCode
you can use.value.hashCode
. Also note that for deep comparison is used bothhashCode
andequals
.
Any scalajson.ast.JValue
implements a conversion to scalajson.ast.unsafe.JValue
with a toUnsafe
method and vice versa with a
toStandard
method. These conversion methods have been written to be as fast as possible.
There are some peculiarities when converting between the two AST's. When converting a scalajson.ast.unsafe.JNumber
to a
scalajson.ast.JNumber
, it is possible for this to fail at runtime (since the internal representation of
scalajson.ast.unsafe.JNumber
is a String
and it doesn't have a runtime check). It is up to the caller on how to handle this error (and when),
a runtime check is deliberately avoided on our end for performance reasons.
Converting from a scalajson.ast.JObject
to a scalajson.ast.unsafe.JObject
will produce
an scalajson.ast.unsafe.JObject
with an undefined ordering for its internal Array
/js.Array
representation.
This is because a Map
has no predefined ordering. If you wish to provide ordering, you will either need
to write your own custom conversion to handle this case. Duplicate keys will also be removed for the same reason
in an undefined manner.
Do note that according to the JSON spec, whether to order keys for a JObject
is not specified. Also note that Map
disregards ordering for equality, however Array
/js.Array
equality takes ordering into account.
ScalaJSON JNumber
provides conversions to various number types with the following conventions
toInt
: Safe conversion toInt
which accounts for values such as1.0
and100.00e-2
(which both evaluate to1
). Also safely detects over/underflow.toLong
: Safe conversion toLong
which accounts for values such as1.0
and100.00e-2
(which both evaluate to1
). Also safely detects over/underflow.toDouble
: Converts to aDouble
assuming the same semantics ofDouble
(i.e. precision loss is expected).toFloat
: Converts to aFloat
assuming the same semantics ofFloat
(i.e. precision loss is expected).toBigInt
: Converts to aBigInt
which accounts for values such as1.0
and100.00e-2
(which evaluates to1
). Can construct aBigInt
for as much as memory as the system has (if your system runs out of memory this is considered undefined behaviour).toBigDecimal
: Converts to aBigDecimal
with all of the caveats ofBigDecimal
construction. TheBigDecimal
is constructed withMathContext.UNLIMITED
precision.
With the .toFloat
and .toDouble
methods, if you don't want any loss in precision, its advisable to convert to
BigDecimal
first and then work from there, i.e. when working with Decimal
/Float
, its implied that you will
have loss of precision.
Remember that in all cases if these methods are not applicable, you can always use the .value
field to get the
original string representation of the number.
ScalaJSON also provides support for Scala.js.
The usage of Scala.js mirrors the usage of Scala on the JVM however Scala.js also implements
a .toJsAny
method which allows you to convert any
scalajson.ast.JValue
/scalajson.ast.unsafe.JValue
to a Javascript value in Scala.js
.
Note that, since a scalajson.ast.JNumber
/scalajson.ast.unsafe.JNumber
is unlimited
precision (represented internally as a String
), calls to .toJsAny
can lose precision on the
underlying number (numbers in Javascript are represented as double precision floating point number).
You can use the .value
method on a scalajson.ast.JNumber
/scalajson.ast.unsafe.JNumber
to
get the raw string value as a solution to this problem.
Further, toFloat
on JNumber
(see Number Conversions ) can have different semantics on Scala.js, depending on whether you have
strict-floats enabled in your application. Please see the Scala.js semantics page
for more information.
scalajson.JNumber
uses jNumberRegex
to validate whether a number is a valid
JSON number. One can use jNumberRegex
explicitly if you want to use the validation that
is used by scalajson.JNumber
(for example, you may want to validate proper numbers
before creating a scalajson.unsafe.JNumber
).
import scalajson.jNumberRegex
"3535353" match {
case jNumberRegex(_ *) => true
case _ => false
}
ScalaJSON uses the Scala Code of Conduct for all communication and discussion. This includes both GitHub, Gitter chat and other more direct lines of communication such as email.
The project is formatted using scalafmt. Please run scalafmt
in SBT before committing any changes