Lift-mongo-record: Empty JsonObjectField in mongo collection - json

I'm trying to save a record with JsonObjectField (using lift-mongo-
record in Play framework) but it is empty in database collection.
That's my code:
Define classes:
class Wish extends MongoRecord[Wish] with MongoId[Wish] {
def meta = Wish
object body extends StringField(this, 1024)
object tags extends MongoListField[Wish, String](this)
object form extends JsonObjectField[Wish, Criterion](this, Criterion) {
def defaultValue = null.asInstanceOf[Criterion]
}
}
object Wish extends Wish with MongoMetaRecord[Wish] {
override def collectionName = "wishes"
}
case class Criterion (key: String, value: String) extends JsonObject[Criterion] {
def meta = Criterion
}
object Criterion extends JsonObjectMeta[Criterion]
I try to save record in that way:
Wish.createRecord.body("body").tags(List("tags", "test")).form(new Criterion("From", "Arbat")).save
And in mongodb collection I have smth like:
{ "_id" : ObjectId("4dfb6f45e4b0ad4484d3e8c6"), "body" : "body", "tags" : [ "tags", "test" ], "form" : { } }
What am I doing wrong?

Your code looks fine. Maybe try using MongoCaseClassField like:
object form extends MongoCaseClassField[Wish, Criterion](this)
case class Criterion (key: String, value: String){}
//btw you can leave out the "new" for case classes - it calls
//the automatically generated apply method
Wish.createRecord.body("body").tags(List("tags", "test"))
.form(Criterion("From","Arbat")).save
Here's an example for MongoCaseClassListField:
object form extends MongoCaseClassListField[Wish, Criterion](this)
Wish.createRecord.body("body").tags(List("tags","test"))
.form(List(Criterion("key1","val1"), Criterion("key2", "val2"))
.save

Related

Scala-Gson Deserialization: Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?

Exception in thread "main" java.lang.UnsupportedOperationException: Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?
I get that error message as I am trying to use gson deserialization in scala. i am new to scala
I have an abstract class
abstract class Pie() {
//does something
}
I also have another class
class Apple (val apple: someclass) extends makeApplePie {
// does something
}
class Peach (val peach: someclass) extends makePeachPie{
//does something
}
the abstract class makeApplePie
abstract class makeApplePie extends Pie{
//does something
}
abstract class makePeachPie extends Pie{
//does something
}
def getTypeOfPieFromString (test:String): Class[_ <: Pie]= {
test match {
case "Apple" => classOf[makeApplePie]
case "Peach" => classOf[makePeachPie]
case _=> classOf[makeApplePie]
}}
so I want receive some input from a yaml file and i am using gson.json to parse the input to scala
I dont know what kind of fruit will be added by user so I am using Class [_ <: Pie]
The question is how will i register typeAdapter for a Class[_ <:Pie]
new GsonBuilder().
registerTypeAdapter(
class[_ <:Pie],
new JsonDeserializer[_ <:Pie] {
override def deserialize(jsonElement: JsonElement, type: Type, context:JsonDeserializationContext): Class [_ <: Pie] = {
val name = jsonElement.getAsString
//some function that gets class makeApplePie or makePeachPie
getTypeOfPieFromString(name)
}
}
)
this is where parsing is done
case class howManyPie (pieType : [_ <:Pie])
case class PieInput(InputPieType: java.util.HashMap[String, howManyPie ])
// some parsing is done
val bakePie = new startPie(
//takes in a list in case user wants to bake multiple pies
pies = JavaConverters.iterableAsScalaIterable(pieInput.inputPieType.values()).toList)
Can anybody help?

Circe : Serialize case class body fields to JSON

I have a case class with some val (which is not a constructor param). How can I get those fields also in the generated json ?
I was using Json4s before, and used FieldSerializer which did this trick. But unable to get this with Circe.
What I want is to define all the required fields in a trait, sometimes, the field may be a part of the case class. But there are cases, where it doesn't make sense to keep them as part of case class, but still needed in the json.
Please note the difference between EntityWithBodyParams and AnotherEntity below.
Here is my sample case class.
trait NamedEntity {
def name:String
}
case class EntityWithBodyParams(id:Long) extends NamedEntity {
override val name:String = "Name"
}
case class AnotherEntity(id:Long, name:String) extends NamedEntity
Response after asJson
{
"id" : 100
}
But my expectation is :
{
"id" : 100,
"name":"Name"
}
You can create your own Encoder.
import io.circe.{Encoder, Json}
case class EntityWithBodyParams(id: Long) {
val name: String = "Name"
}
implicit val encoder: Encoder[EntityWithBodyParams] = new
Encoder[EntityWithBodyParams] {
override def apply(entity: EntityWithBodyParams): Json = Json.obj(
"id" -> Json.fromLong(entity.id),
"name" -> Json.fromString(entity.name)
)
}
Reason for this behavior is fact that circe auto encoder uses only product fields of case class. More info you can find here https://github.com/milessabin/shapeless
Try writing your case class like this instead.
case class EntityWithBodyParams(id:Long, val name:String = "Name")

How can I set a field to be kept as plain while Finatra is parsing a request into a case class?

I have a controller handling a route like 'POST /doit', the json body is automatically parsed into a case class using Finatra built in tools (Jackson, etc), something like this:
class MyController extends Controller {
post("/doit") { request: MyRequest =>
// something
}
}
case class MyRequest(
id: String,
custom: String
)
Here are some valid requests:
{ "id": "my id", "custom": "my custom" }
{ "id": "my id", "custom": "{'x': 'y'}" }
As you can see, 'custom' field can be a JSON which can't be deserialized because Jackson expect it to be a POJO instead of a String, I tried wrapping this JSON with quotes but they are ignored and the field is handled as JSON.
How can I let Jackson library to know that this field should be kept plain?
I had read and the best solution I came up with is to write a custom deserializer, in this case, I have not idea how to integrate with Finatra.
As "Ryan O'Neill" pointed out in Finatra Google Group, there are examples for writing a custom deserializer in ExampleCaseClasses.scala.
I'm copying the following code from previous scala source:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
case class CaseClassWithCustomDecimalFormat(
#JsonDeserialize(using = classOf[MyBigDecimalDeserializer])
myBigDecimal: BigDecimal,
#JsonDeserialize(using = classOf[MyBigDecimalDeserializer])
optMyBigDecimal: Option[BigDecimal])
class MyBigDecimalDeserializer extends JsonDeserializer[BigDecimal] {
override def deserialize(jp: JsonParser, ctxt: DeserializationContext): BigDecimal = {
val jsonNode: ValueNode = jp.getCodec.readTree(jp)
BigDecimal(jsonNode.asText).setScale(2, RoundingMode.HALF_UP)
}
override def getEmptyValue: BigDecimal = BigDecimal(0)
}
Thanks Ryan.

How to model finite set of values of enum-like type for (de)serialization?

I am using Spray-json 1.3.1. I have the following JSON message:
{
"results": [{
... NOT IMPORTANT PART HERE ...
}],
"status": "OK"
}
Trivially, this can be deserialized to status String field via
case class Message[T](results: List[T], status: String)
with custom Protocol
object MessageProtocol extends DefaultJsonProtocol {
implicit def messageFormat[T: JsonFormat] = jsonFormat2(Message.apply[T])
}
Since status field can be one of OK, ZERO_RESULTS, OVER_QUERY_LIMIT having this field as a String makes no sense. As I am coming from
Java background I tried enums in Scala implemented as follows:
case class Message[T](results: List[T], status: Status)
object Status extends Enumeration{
type Status = Value
val OK,ZERO_RESULTS,OVER_QUERY_LIMIT, REQUEST_DENIED, INVALID_REQUEST,UNKNOWN_ERROR = Value
}
object MessageProtocol extends DefaultJsonProtocol {
implicit val statusFormat = jsonFormat(Status)
implicit def messageFormat[T: JsonFormat] = jsonFormat2(Message.apply[T])
}
What is best practice/approach to solve this?
You can simply implement your own RootJsonFormat (as an implicit in Message companion object) and override read and write functions. There you will have JsObject and you can convert it to your own case class as you want like converting the string to desired enumeration etc. You can see a sample here

Scala TabbedPane Custom Page Subclass

I have a problem with my custom subclass of Page. Here is a short example.
def main(args: Array[String]): Unit = {
val pane = new TabbedPane
pane.pages += new LanguagePage("common_yes", new Label)
println(pane.pages(0).isInstanceOf[LanguagePage])
}
class LanguagePage(languageKey: String, com: Component)
extends Page("", com, null) {
def method() {...}
}
When I run the program, false is printed. It would be nice to know why this happens and how I can access my added page again. In my case I need to run the method that the subclass has to adjust the title string based on the language that is set.
If you look at the source
class TabbedPane extends Component with Publisher {
object pages extends BufferWrapper[Page] {
def apply(n: Int) =
new Page(TabbedPane.this, peer.getTitleAt(n),
UIElement.cachedWrapper[Component]
(peer.getComponentAt(n).asInstanceOf[javax.swing.JComponent]),
peer.getToolTipTextAt(n))
you always get a new instance.
Looking at
class TabbedPane extends Component with Publisher {
object pages extends BufferWrapper[Page] {
def +=(t: Page): this.type = {
t.parent = TabbedPane.this
peer.addTab(t.title, null, t.content.peer, t.tip)
this
}
the argument t is not recorded at all.
But you can change the title using
pane.pages(0).title = "Title 2"
so you could define a logical page (a companion in addition to the Swing page) separately. Using a reference to the pane and the tab index this class can change the title.