json4s always escapes unicode character € - scala

I try pretty-writing a JString containing a € character with json4s as follows:
import org.joda.time.format.ISODateTimeFormat
import org.joda.time.{DateTime, DateTimeZone}
import org.json4s.native.Serialization.writePretty
import org.json4s.{DateFormat, DefaultFormats, Formats, JString}
import java.util.{Date, TimeZone}
object Json4sEncodingTest {
val formats = new Formats {
val dateFormat: DateFormat = new DateFormat {
override def parse(s: String): Option[Date] =
try {
Option(
DateTime
.parse(s, ISODateTimeFormat.dateTimeParser().withZoneUTC())
.withZone(DateTimeZone.forID(timezone.getID))
.toDate
)
} catch {
case e: IllegalArgumentException => None
}
override def format(d: Date): String = DefaultFormats.lossless.dateFormat.format(d)
override def timezone: TimeZone = DefaultFormats.lossless.dateFormat.timezone
}
override def alwaysEscapeUnicode: Boolean = false
}
def main(args: Array[String]): Unit = {
println(writePretty(JString("2€"))(formats))
}
}
This results in:
"2\u20ac"
My expected result would be:
"2€"
I found that in org.json4s.ParserUtil.quote characters between \u2000 and \u2100 are always escaped.
Question: Why is this the case?
json4s version: 3.7.0-M7
scala version: 2.12.11

As elaborated in this github issue, it is impossible currently to do this using json4s native. The code that checks if to escape or not is:
(c >= '\u0000' && c <= '\u001f') || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')
while € doesn't satisfy this condition. One possible solution (well, sort of solution) is using jackson instead of native. Then this will work:
import org.json4s.jackson.JsonMethods._
import org.json4s.JsonAST.JString
println(pretty(render(JString("2€"))))
Code run at Scastie.

Related

How to use Array in JCommander in Scala

I want to use JCommander to parse args.
I wrote some code:
import com.beust.jcommander.{JCommander, Parameter}
import scala.collection.mutable.ArrayBuffer
object Config {
#Parameter(names = Array("--categories"), required = true)
var categories = new ArrayBuffer[String]
}
object Main {
def main(args: Array[String]): Unit = {
val cfg = Config
JCommander
.newBuilder()
.addObject(cfg)
.build()
.parse(args.toArray: _*)
println(cfg.categories)
}
}
Howewer it fails with
com.beust.jcommander.ParameterException: Could not invoke null
Reason: Can not set static scala.collection.mutable.ArrayBuffer field InterestRulesConfig$.categories to java.lang.String
What am i doing wrong?
JCommander uses knowledge about types in Java to map values to parameters. But Java doesn't have a type scala.collection.mutable.ArrayBuffer. It has a type java.util.List. If you want to use JCommander you have to stick to Java's build-in types.
If you want to use Scala's types use one of Scala's libraries that handle in in more idiomatic manner: scopt or decline.
Working example
import java.util
import com.beust.jcommander.{JCommander, Parameter}
import scala.jdk.CollectionConverters._
object Config {
#Parameter(names = Array("--categories"), required = true)
var categories: java.util.List[Integer] = new util.ArrayList[Integer]()
}
object Hello {
def main(args: Array[String]): Unit = {
val cfg = Config
JCommander
.newBuilder()
.addObject(cfg)
.build()
.parse(args.toArray: _*)
println(cfg.categories)
println(cfg.categories.getClass())
val a = cfg.categories.asScala
for (x <- a) {
println(x.toInt)
println(x.toInt.getClass())
}
}
}

Scala Function to return True/False when a date is present in a list

I am trying to create a function where I would to pass a date as a String and would like to check if its previous date is present in a list that is passed as second parameter to the function. If yes then it should return a boolean.
def fn1(date: String, days: Seq[String]): Boolean = {
....
....
}
Also, I need a similar function that extracts the day from the previous day of the input parameter and then looks it up in a list. This function also returns a boolean.
def fn2(date: String, days: Seq[String]): Boolean = {
....
....
}
How can I implement this in Scala?
Your question is confused and confusing. I don't understand the difference between fn1() and fn2().
Still, maybe this will help.
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
def fn(date: String, days: Seq[String]): Boolean = {
val dtFormat = DateTimeFormatter.ofPattern("<expected format here>")
days.contains(LocalDateTime.parse(date, dtFormat)
.minusDays(1)
.format(dtFormat))
}
First of all, don't pass dates around as Strings, use java.time.LocalDate.
def fn1(date: LocalDate, days: Seq[LocalDate]): Boolean =
days.contains(date.minusDays(1))
I think this solution can solve your problem. Based on your date format just change the split("-") delimiter.
import java.text.SimpleDateFormat
import java.util.Calendar
object Driver{
def getPreviousDate(date:String):String={
val cal=Calendar.getInstance()
val dateArr=date.split("-")
val day=dateArr(0).trim.toInt
val month=dateArr(1).trim.toInt
val year=dateArr(2).trim.toInt
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, month-1)
cal.set(Calendar.DAY_OF_MONTH, day-1)
val sdf=new SimpleDateFormat("dd-MM-yyyy")
sdf.format(cal.getTime)
}
def fn1(date: String, days: Seq[String]): Boolean = {
val prevDate=getPreviousDate(date)
days.contains(prevDate)
}
def fn2(date: String, days: Seq[String]): Boolean = {
val previousDate=getPreviousDate(date)
val previousDay=previousDate.split("-")(0).trim.toInt
val daysList=days.map(day=>day.split("-")(0).trim.toInt)
daysList.contains(previousDay)
}
def main(arr:Array[String]) {
val days=Seq("10-11-2018","11-11-2018","12-11-2018","13-11-2018")
val date="15-11-2018"
val status=fn1(date,days)
println(status)
val status2=fn1(date,days)
println(status2)
}
}
This is the working code, thanks to all for your help.
def isHoliday(date: String,
days: Seq[String],
holidays: List[String]): Boolean = {
val dtFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val dayFormat = DateTimeFormatter.ofPattern("EEEE")
days.contains(LocalDate.parse(date, dtFormat).minusDays(1).format(dayFormat)) ||
holidays.contains(LocalDate.parse(date, dtFormat).minusDays(1).format(dtFormat))
}
** need to change the string dates to LocalDate.

Marshalling java.util.Date with SprayJson

I am new to Scala and Akka.
I have the following case class:
case class Demo(userId: String, date: java.util.Date, message: String) extends BusinessModel
I have to send List[Demo] in Json format as response to a get request but I am facing problem in the following code due to Date:
implicit val demoFormat: RootJsonFormat[Demo] = jsonFormat3(Demo)
I would be grateful if you may kindly help me out
You need to provide a format for java.util.Date, since spray doesn't have one by default.
A quick google search leads to https://gist.github.com/owainlewis/ba6e6ed3eb64fd5d83e7 :
import java.text._
import java.util._
import scala.util.Try
import spray.json._
object DateMarshalling {
implicit object DateFormat extends JsonFormat[Date] {
def write(date: Date) = JsString(dateToIsoString(date))
def read(json: JsValue) = json match {
case JsString(rawDate) =>
parseIsoDateString(rawDate)
.fold(deserializationError(s"Expected ISO Date format, got $rawDate"))(identity)
case error => deserializationError(s"Expected JsString, got $error")
}
}
private val localIsoDateFormatter = new ThreadLocal[SimpleDateFormat] {
override def initialValue() = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX")
}
private def dateToIsoString(date: Date) =
localIsoDateFormatter.get().format(date)
private def parseIsoDateString(date: String): Option[Date] =
Try{ localIsoDateFormatter.get().parse(date) }.toOption
}
Import DateMarshalling._ in piece code where you have wrote implicit val demoFormat: RootJsonFormat[Demo] = jsonFormat3(Demo) and it should be ok now :)

How do I validate a timestamp in scala?

my application takes in a string like this "2002-10-15 10:55:01.000000". I need to validate inside scala script that the string is a valid for a db2 timestamp.
In general (I'd guess) you would do it moslty the same way as in java with either java.text.DateFormat or joda.time.DateTimeFormat (see Joda time).
A simple example:
import java.text.SimpleDateFormat
import java.util.Date
import scala.util.Try
val date = "2002-10-15 10:55:01.000000"
val formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSS")
val test = Try[Date](formatter.parse(date))
would give you:
test: scala.util.Try[java.util.Date] = Success(Tue Oct 15 10:55:01 CEST 2002)
Then you could match:
test match {
case Success(date) => // ok
case Failure(exception) => // not ok
}
You should use SimpleDateFormat of java to do that in scala :
object DateParser {
def isValid(f : String, d : String) = {
try {
val format = new java.text.SimpleDateFormat(f)
format.parse(d)
}catch(java.text.ParseException e) {
false
}
}
def main(args : Array[String]) {
val format = "yyyy-MM-dd k:m:s"
println(isValid(format,"2002-10-15 10:55:01.000000"))
println(isValid(format,"2002-10-1510:55:01.000000"))
}
}

How to handle optional fields in JSON parsing in play 2.1

I'm using the new Play 2.1-RC1 framework and I have a class that has an Option[] field, something like this:
import play.api.libs.json._
import play.api.libs.json.util._
import play.api.libs.json.Reads._
import play.api.libs.json.Writes._
import play.api.libs.json.Format._
import play.api.libs.functional.syntax._
case class Test(name: String, value: Option[String])
object Test {
implicit val testFormat = (
(__ \ "name").format[String] and
(__ \ "value").format[Option[String]]
)(Test.apply, unlift(Test.unapply))
def fromJson(js: String): Test = {
Json.fromJson[Test](Json.parse(js)).fold(
valid = { t => t},
invalid = { e => {
val missingField = (e(0)._1).toString.substring(1)
val badJs = js.trim
val newJs = badJs.substring(0, badJs.length()-1)+",\""+missingField+"\":null}"
fromJson(newJs)
}}
)
}
}
I want to be able to handle JSON strings that omit the optional "value" data, e.g.
val y = """{"name":"someone"}"""
(edited question)
I can rewrite the json string (rather clumsily) as shown in the validation step, but
is there a simpler pattern I can use to supply None for missing Optional fields? Note that this rewrite does not work with nested structures, or anywhere where I can't simply append the missing field.
You can simply do this:
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class Test(name: String, value: Option[String])
implicit val testFormat = Json.format[Test]
def hoge = Action(Json.parse.json) { request =>
Json.fromJson[Test](request.body)
...
}
OK ... so the answer is very simple. Use
fomatOpt()
for optional fields. So the test formatter now looks like this:
import play.api.libs.json._
import play.api.libs.json.util._
import play.api.libs.json.Reads._
import play.api.libs.json.Writes._
import play.api.libs.json.Format._
import play.api.libs.functional.syntax._
case class Test(name: String, value: Option[String])
object Test {
implicit val testFormat = (
(__ \ "name").format[String] and
(__ \ "value").formatOpt[String]
)(Test.apply, unlift(Test.unapply))
def fromJson(js: String): Test = {
Json.fromJson[Test](Json.parse(js)).fold(
valid = { t => t},
invalid = { e => {
println("BAD JSON!")
null
}}
)
}
}