Is there some elegant way to convert the json data(based on case class) to tsv form?
I have a case class that has nested case class and nested case class can have list and map.
case class Product (
pname: Option[String],
pid: Int,
pDetail: Option[PDetail]
)
case class PDetail (
pbatchNo: List[Int]
)
example json:
{
"pname" : "pnameValue",
"pid" : "pidValue",
"pDetail":
{
"pbatchNo" : [1,2]
}
}
I want a output like:
pnameValue pidValue 1 2
You can override the toString method and separate the field by tab \t.
Something like:
override def toString: String = {
s"pnameValue:$pnameValue\tpidValue:$pidValue"
}
Related
I want to print the contents of a collection and I've tried with the mkString method, but it gives me still not the right content of the object.
My code:
package org.template
import org.apache.predictionio.controller.LServing
class Serving
extends LServing[Query, PredictedResult] {
override
def serve(query: Query,
predictedResults: Seq[PredictedResult]): PredictedResult = {
println(predictedResults.mkString("\n"))
predictedResults.head
}
}
The response:
predictedResult([Lorg.template.ItemScore;#2fb3a837,[Lorg.template.Rule;#5cfc70a8)
Definition of the PredictedResult class:
package org.template
import org.apache.predictionio.controller.EngineFactory
import org.apache.predictionio.controller.Engine
// Query most similar (top num) items to the given
case class Query(items: Set[String], num: Int) extends Serializable
case class PredictedResult(itemScores: Array[ItemScore], rules: Array[Rule]) extends Serializable
If PredictedResult is a case class like so
case class PredictedResult(value: String)
val predictedResults = List(PredictedResult("aaa"), PredictedResult("bbb"))
println(predictedResults.mkString("\n"))
then we get nice output
PredictedResult(aaa)
PredictedResult(bbb)
However if it is a regular class like so
class PredictedResult(value: String)
val predictedResults = List(new PredictedResult("aaa"), new PredictedResult("bbb"))
println(predictedResults.mkString("\n"))
then we get
example.Hello$PredictedResult#566776ad
example.Hello$PredictedResult#6108b2d7
To get the nice output for regular class we need to override its toString method like so
class PredictedResult(value: String) {
override def toString: String = s"""PredictedResult($value)"""
}
which now outputs
PredictedResult(aaa)
PredictedResult(bbb)
Addressing the comment we have
case class Rule(v: String)
case class ItemScore(v: Int)
case class PredictedResult(itemScores: Array[ItemScore], rules: Array[Rule]) {
override def toString: String =
s"""
|PredictedResult(Array(${itemScores.mkString(",")}, Array(${rules.mkString(",")}))
""".stripMargin
}
val predictedResults = List(PredictedResult(Array(ItemScore(42), ItemScore(11)), Array(Rule("rule1"), Rule("rule2"))))
println(predictedResults.mkString("\n"))
which outputs
PredictedResult(Array(ItemScore(42),ItemScore(11), Array(Rule(rule1),Rule(rule2)))
If we change from Array to List like so
case class Rule(v: String)
case class ItemScore(v: Int)
case class PredictedResult(itemScores: List[ItemScore], rules: List[Rule])
val predictedResults = List(PredictedResult(List(ItemScore(42), ItemScore(11)), List(Rule("rule1"), Rule("rule2"))))
println(predictedResults.mkString("\n"))
then we get nice output out-of-the-box without the need to override toString
PredictedResult(List(ItemScore(42), ItemScore(11)),List(Rule(rule1), Rule(rule2)))
{
"cars": [{
"amount": 120.00,
"name": "Car1"
}, {
"amount": 245.00,
"name": "Car2"
}]
}
I am reading above JSON as following in my Controller
val body: JsObject = request.body.asInstanceOf[JsObject]
I am having following CASE CLASS
case class BIC(name: String, amount: Double)
I want to create List[BIC] objects by reading data from JSON [e.g. body] using Functional style
Use Play JSON.
Example:
case class Wrapper(cars: List[Bic])
case class BIC(name: String, amount: Double)
Then in your controller:
implicit val wrapperFormats = Json.format[Wrapper]
implicit val bICFormats = Json.format[BIC]
def postCars(): Action[JsValue] = Action(json.parse) { implicit request =>
request.body.validate[Wrapper] match {
case JsSuccess(obj, _) => {
//do something with obj.
}
case JsError(err) => {
BadRequest(
JsObject(
"error" -> err.toString
)
)
}
}
}
Please note that I am returning Action[JsValue] this is so JQuery will run success when using AJAX.
I hope this helps,
Rhys
another reference:
https://www.playframework.com/documentation/2.5.x/ScalaJsonCombinators
First, define two case classes for your model like this :
object Models {
case class Bic(name : String, amount : Double)
object Bic {
implicit val BicFormat = Json.format[Bic]
}
case class Cars(bics : List[Bic])
object Cars {
implicit val CarsFormat = Json.format[Cars]
}
}
You're using the Play Framework so you can use the JSON library.
In your controller, if you want to read the bics, you can do it like that :
def getCars = Action(parse.json) { request =>
request.body.validate[Cars] map { cars =>
// treat your cars ..
}
}
I'm use lift-json render a bson string with class extractor, after that, use mongo Document class constructor a document instance with that bson string.
A problem is how about represent $or bson.It seems not a classic json array.
{"$or": [
{"username": "administrator"},
{"phone":"110"},
{"email":"123#xxx.com"},
{"pen_name":"lorancechen"}
]}
How to use lift class extractor represent this bson array?
Besides, the reason of use string between app and mongo is they are communicate under a simple socket.
UPDATE add a example
extractor a normal array class as follow:
import net.liftweb.json._
import net.liftweb.json.Extraction._
case class Name(name: String)
case class JsonArray(array:List[Name])
object JsonClient extends App {
implicit val formats = DefaultFormats
val names = Name("jone01") :: Name("jone02") :: Nil
val array = JsonArray(names)
val jsonString = prettyRender(decompose(array))
println(jsonString)
}
OUTPUT:
{
"array":[
{
"name":"jone01"
},
{
"name":"jone02"
}
]
}
How to represent this
{"$or": [
{"username": "administrator"},
{"phone":"110"},
{"email":"123#xxx.com"},
{"pen_name":"lorancechen"}
]}
every field key (eg, username, phone) of element inner "$or" is not common key name and I haven't find a way to represent it use class template.
I don't get it why your JSON structure is that way, maybe what you want is the following one:
{
"$or": [
{
"username": "administrator", "phone":"110",
"email":"123#xxx.com", "pen_name":"lorancechen"
},
{
"username": "xxx", "phone":"xxx",
"email":"xxx", "pen_name":"xxx"
}
...
]
}
Actually Lift provides such tools and the downside is that the implementation is a little bit ugly.
import net.liftweb.mongodb.{JsonObjectMeta, JsonObject}
// the trait here is for unifying the type of
//four case classes i.e Username, Phone....
sealed trait PersonField
object Username extends JsonObjectMeta[Username]
case class Username(username: String) extends JsonObject[Username]
with PersonField {
def meta = Username
}
case class Phone(phone: String) extends JsonObject[Phone] with PersonField {
def meta = Phone
}
object Phone extends JsonObjectMeta[Phone]
case class Email(email: String) extends JsonObject[Email] with PersonField {
def meta = Email
}
object Email extends JsonObjectMeta[Email]
case class PenName(pen_name: String) extends JsonObject[PenName]
with PersonField {
def meta = PenName
}
object PenName extends JsonObjectMeta[PenName]
case class Output(`$or`: List[PersonField]) extends JsonObject[Output] {
def meta = Output
}
object Output extends JsonObjectMeta[Output]
object JsonClient extends App {
val username = Username("administrator")
val phone = Phone("110")
val email = Email("123#xxx.com")
val penName = PenName("lorancechen")
val outPut = Output(username :: phone :: email :: penName :: Nil)
import net.liftweb.json._
implicit val formats = DefaultFormats
import net.liftweb.json.{JsonAST, Printer}
val result = Printer.pretty(JsonAST.render(outPut.asJObject))
/*
{
"$or":[{
"username":"administrator"
},{
"phone":"110"
},{
"email":"123#xxx.com"
},{
"pen_name":"lorancechen"
}]
}
*/
println(result)
}
Anyway, hope it helps.
Given the following BSONDocument...
val values = BSONDocument("values" -> BSONArray("one", "two", "three"))
How do I convert it to a List? I've tried this...
values.getAs[List[String]]("values").getOrElse(List.empty))
... but it doesn't work - I always get List.empty.
Am I missing something?
EDIT
OK... I think it is worth describing the real case. I ran the distinct command and this was the result:
values: {
values: [
0: BSONObjectID("55d0f641a100000401b7e454")
],
stats: {
n: BSONInteger(1),
nscanned: BSONInteger(1),
nscannedObjects: BSONInteger(1),
timems: BSONInteger(0),
cursor: BSONString(BtreeCursor projectId_1)
},
ok: BSONDouble(1.0)
}
I need to transform values to a Scala List[String] like this:
List("55d0f641a100000401b7e454")
Here is my solution. First, I've defined a BSONReader[BSONValue, String] like this...
package object bsonFormatters {
implicit object BSONValueStringReader extends BSONReader[BSONValue, String] {
def read(bson: BSONValue) = bson match {
case oid: BSONObjectID => oid.stringify
}
}
... and then just imported it in my companion object like this:
import reactivemongo.bson.{BSONString, BSONDocument}
import reactivemongo.core.commands.{CommandError, BSONCommandResultMaker, Command}
case class Distinct(
collectionName: String,
field: String,
query: Option[BSONDocument] = None
) extends Command[Seq[String]] {
override def makeDocuments = BSONDocument(
"distinct" -> BSONString(collectionName),
"key" -> field,
"query" -> query
)
val ResultMaker = Distinct
}
object Distinct extends BSONCommandResultMaker[Seq[String]] {
import bsonFormatters._ // this makes the trick
def apply(document: BSONDocument) = CommandError.checkOk(
document,
Some("distinct")
).toLeft(
document.getAs[List[String]]("values").getOrElse(List.empty)
)
}
I hope it helps.
I have the following model
class Recording private() extends MongoRecord[Recording] with ObjectIdPk[Recording] {
def meta = Recording
object data extends StringField(this, 50)
}
I'm currently saving a Json object as a string in the "data" field, I've used JsonObject field before but only with predefined object structures. In this case the json object being saved can have any structure or data fields so a predefined data structure is not an option.
Say I have:
{"name" : "James", "value" : "Hai!"}
Or
{"result" : 1, "handle" : "lorem_ipsum"}
I need to be able to save both as a json object in the same field, "data".
Is there a way I can do this?
Thanks in advance for any help, much appreciated :)
What might work for you is storing the data as a JValue rather than a String. You could use a JsonObjectField wrapping a case class that contains a JValue, which would allow an arbitrary structure, but it will give you an extra level of nesting in Mongo. To get around that, how about creating a custom field just to hold a JValue?
One stab at it:
abstract class JValueObjectField[OwnerType <: BsonRecord[OwnerType]](rec: OwnerType)
extends Field[JValue,OwnerType]
with MandatoryTypedField[JValue]
with MongoFieldFlavor[JValue] {
def owner = rec
def defaultValue = JNothing
def toForm: Box[NodeSeq] = Empty
implicit val formats = owner.meta.formats
def asJValue: JValue = valueBox openOr JNothing
def setFromJValue(jv: JValue): Box[JValue] = Full(jv)
def setFromString(in: String): Box[JValue] = tryo(JsonParser.parse(in)) match {
case Full(jv: JValue) => setFromJValue(jv)
case f: Failure => setBox(f)
case other => setBox(Failure("Error parsing String into a JValue: "+in))
}
def setFromAny(in: Any): Box[JValue] = in match {
case dbo: DBObject => setFromDBObject(dbo)
case value: JValue => setBox(Full(value))
case Some(value: JValue) => setBox(Full(value))
case Full(value: JValue) => setBox(Full(value))
case (value: JValue) :: _ => setBox(Full(value))
case s: String => setFromString(s)
case Some(s: String) => setFromString(s)
case Full(s: String) => setFromString(s)
case null|None|Empty => setBox(defaultValueBox)
case f: Failure => setBox(f)
case o => setFromString(o.toString)
}
def asDBObject: DBObject = JObjectParser.parse(asJValue.asInstanceOf[JObject])
def setFromDBObject(dbo: DBObject): Box[JValue] =
setFromJValue(JObjectParser.serialize(dbo))
}
...which looks a lot: all I've done is cut and paste from JValueObjectField with one parameter removed and fixed at JValue. There may be a smarter way to do that.
You can then use this in your model:
object data extends JValueObjectField(this)
I'd populate it using the lift-json DSL:
val json = ("name" -> "Bob") ~ ("result" -> 1)
Recording.createRecord.data(json).save
This will give you something in your MongoDB document like this:
"data" : {
"name" : "Bob",
"result" : 1
}
BTW, the Lift Mailing list is a good way to get a better answer: that just happens to be where most of the Lift-related people seem to congregate.
class Recording private() extends MongoRecord[Recording] with ObjectIdPk[Recording] {
def meta = Recording
object data extends JObjectField(this)
}