I have the following case:
class MyClass (object):
#property
def _value(self):
return self.value * 5
mapper(MyClass, myTableMetadata, properties = {
"value": synonym("_value", map_column=False)
})
I'm completely aware why this is leading to recursion, but is there a way from the class to access the self.value property explicitly, not through the mapper?
Related
We are using def for declaring variables and defining functions with
def myVariable
def getMyVar = {}
def getMyVariable () {}
What are these def defines? And how it identifies either function/ closure or method?
When using def keyword in Groovy, the actual type holder is Object so you can assign any object to variables defined with def, and return any kind of object if a method is declared returning def. def keyword is used for untyped variables whose type is defined dynamically at runtime.
Here are 3 definitions using def and the equivalent with Object as the type. The first defines an untyped variable and the second defines a variable assigned with an empty closure. The third defines the return type of a method.
def myVariable // Object myVariable
def getMyVar = {} // Object getMyVar = {}
def getMyVariable() {} // Object getMyVariable() {}
Note if assigning primitive number values (e.g int, float, etc.) to a def variable, it will auto-convert primitive types to an object instance of class Integer, Float, etc.
The Groovy Style Guide (#3 and #21) gives some guidelines on when to use optional typing with def and when to use explicit strong typing.
Using Spray JSON, I would like to be able to parse an array of String, but still be able to deserialize correctly if a single String comes.
That is, with this field:
arrayval: List[String]
and this JSON:
arrayval: ["a", "b"]
it would create a List("a","b"), and with this JSON:
arrayval: "a"
it would create a List("a").
Using default listFormat it would complain in the second case.
Is there a way to configure this kind of flexibility?
In case it helps anyone, I've solved it by overriding the listFormat in the CollectionFormats (which is used in the DefaultJsonProtocol).
trait FlexibleCollectionFormats extends CollectionFormats {
implicit override def listFormat[T: JsonFormat] = new RootJsonFormat[List[T]] {
import spray.json._
def write(list: List[T]) = JsArray(list.map(_.toJson).toVector)
def read(value: JsValue): List[T] = value match {
case JsArray(elements) => elements.map(_.convertTo[T])(collection.breakOut)
case JsString(element) => List[T](new JsString(element).convertTo[T])
case x => deserializationError("Expected List as JsArray, but got " + x)
}
}
}
Then I created my own protocol instead of DefaultJsonProtocol, that basically uses the same ones as Default but overriding the CollectionFormats:
trait FlexibleDefaultJsonProtocol
extends BasicFormats
with StandardFormats
// with CollectionFormats
with FlexibleCollectionFormats
with ProductFormats
with AdditionalFormats
object FlexibleDefaultJsonProtocol extends FlexibleDefaultJsonProtocol
and later you use extends FlexibleDefaultJsonProtocol instead of DefaultJsonProtocol. You can always switch and use one or the other in your classes, so I like the flex it provides.
I have the following use-case:
Each class that I'm serde using JSON4S have a field, named ID. This ID can be any type T <: Stringifiable, where Stringifiable requires your ID type to be hashed to a string. Stringifiables also have constructors that rebuilds them from a string.
I'd like to serde any Stringifiable, for example ComplexIdentifier to a JSON of ID: stringified_identifier. Serialization works nicely, but unfortunately during deserialization, JSON4S is not going to use the default constructor which has only 1 string constructor. It finds the constructor, but if the identifier has a signature of case class ComplexIdentifier(whatever: String), it tries to extract a whatever name from the JString(stringified_identifier). That fails, so MappingException is thrown internally.
Is there any way to teach JSON4S to use the default constructor without extracting the values like this? It would be so obvious to just use the value from the JString and construct the Stringifiable using that.
Thanks!
Use the applymethod in a Companion to overload the constructor for the ID classes with a String parameter. Then just use a custom serializer for all of your ID types
sealed abstract class Stringifiable {}
case class ComplexIdentifier(whatever: List[Long]) extends Stringifiable
case class SimpleIdentifier(whatever: Int) extends Stringifiable
//Overload the default constructor
object ComplexIdentifier {
def apply(s: String):ComplexIdentifier = {
ComplexIdentifier(s.split(",").map(_.toLong).toList)
}
}
case class MyClass(id: ComplexIdentifier, value: String)
Then use a custom serializer:
case object ComplexIdentifierSerializer extends CustomSerializer[ComplexIdentifier] ( formats =>
({
case JString(id) => ComplexIdentifier(id)
case JNull => null
},
{
case x: ComplexIdentifier => JString(x.whatever.mkString(","))
}))
Finally, make sure to include the serializer in the implicit formats:
implicit val formats = DefaultFormats ++ List(ComplexIdentifierSerializer)
println(parse("""
{
"id": "1",
"value": "big value"
}
""").extract[MyClass])
val c = MyClass(ComplexIdentifier("123,456"), "super value")
println(write(c))
I need to serialize/deserialize a Scala class with structure something like the following:
#JsonIgnoreProperties(ignoreUnknown = true, value = Array("body"))
case class Example(body: Array[Byte]) {
lazy val isNativeText = bodyIsNativeText
lazy val textEncodedBody = (if (isNativeText) new String(body, "UTF-8") else Base64.encode(body))
def this(isNativeText: Boolean, textEncodedBody: String) = this((if(isNativeText) str.getBytes("UTF-8") else Base64.decode(textEncodedBody)))
def bodyIsNativeText: Boolean = // determine if the body was natively a string or not
}
It's main member is an array of bytes, which MIGHT represent a UTF-8 encoded textual string, but might not. The primary constructor accepts an array of bytes, but there is an alternate constructor which accepts a string with a flag indicating whether this string is base64 encoded binary data, or the actual native text we want to store.
For serializing to a JSON object, I want to store the body as a native string rather than a base64-encoded string if it is native text. That's why I use #JsonIgnoreProperties to not include the body property, and instead have a textEncodedBody that gets echoed out in the JSON.
The problem comes when I try to deserialize it like so:
val e = Json.parse[Example]("""{'isNativeText': true, 'textEncodedBody': 'hello'}""")
I receive the following error:
com.codahale.jerkson.ParsingException: Invalid JSON. Needed [body],
but found [isNativeText, textEncodedBody].
Clearly, I have a constructor that will work...it just is not the default one. How can I force Jerkson to use this non-default constructor?
EDIT: I've attempted to use both the #JsonProperty and #JsonCreator annotation, but jerkson appears to disregard both of those.
EDIT2: Looking over the jerkson case class serialization source code, it looks like a case class method with the same name as its field will be used in the way that a #JsonProperty would function - that is, as a JSON getter. If I could do that, it would solve my problem. Not being super familiar with Scala, I have no idea how to do that; is it possible for a case class to have a user-defined method with the same name as one of its fields?
For reference, here is the code below that leads me to this conclusion...
private val methods = klass.getDeclaredMethods
.filter { _.getParameterTypes.isEmpty }
.map { m => m.getName -> m }.toMap
def serialize(value: A, json: JsonGenerator, provider: SerializerProvider) {
json.writeStartObject()
for (field <- nonIgnoredFields) {
val methodOpt = methods.get(field.getName)
val fieldValue: Object = methodOpt.map { _.invoke(value) }.getOrElse(field.get(value))
if (fieldValue != None) {
val fieldName = methodOpt.map { _.getName }.getOrElse(field.getName)
provider.defaultSerializeField(if (isSnakeCase) snakeCase(fieldName) else fieldName, fieldValue, json)
}
}
json.writeEndObject()
}
Correct me if I'm wrong, but it looks like Jackson/Jerkson will not support arbitrarily nested JSON. There's an example on the wiki that uses nesting, but it looks like the target class must have nested classes corresponding to the nested JSON.
Anyway, if you're not using nesting with your case classes then simply declaring a second case class and a couple implicit conversions should work just fine:
case class Example(body: Array[Byte]) {
// Note that you can just inline the body of bodyIsNativeText here
lazy val isNativeText: Boolean = // determine if the body was natively a string or not
}
case class ExampleRaw(isNativeText: Boolean, textEncodedBody: String)
implicit def exampleToExampleRaw(ex: Example) = ExampleRaw(
ex.isNativeText,
if (ex.isNativeText) new String(ex.body, "UTF-8")
else Base64.encode(ex.body)
)
implicit def exampleRawToExample(raw: ExampleRaw) = Example(
if (raw.isNativeText) raw.textEncodedBody.getBytes("UTF-8")
else Base64.decode(textEncodedBody)
)
Now you should be able to do this:
val e: Example = Json.parse[ExampleRaw](
"""{'isNativeText': true, 'textEncodedBody': 'hello'}"""
)
You could leave the original methods and annotations you added to make the JSON generation continue to work with the Example type, or you could just convert it with a cast:
generate(Example(data): ExampleRaw)
Update:
To help catch errors you might want to do something like this too:
case class Example(body: Array[Byte]) {
// Note that you can just inline the body of bodyIsNativeText here
lazy val isNativeText: Boolean = // determine if the body was natively a string or not
lazy val doNotSerialize: String = throw new Exception("Need to convert Example to ExampleRaw before serializing!")
}
That should cause an exception to be thrown if you accidentally pass an instance of Example instead of ExampleRaw to a generate call.
I have found following function calls in several frameworks which appear to me as if the framework extends some base classes. Some examples:
within(500 millis)
or
"Testcase description" in
{ .... }
First example returns a duration object with the duration of 500 milliseconds from akka and second is the definition of a testcase from scalatest.
I would like to know how this behavior is achieved and how it is called.
This is done with the "Pimp my library" technique.
To add non existing methods to a class, you define an implicit method that converts objects of that class to objects of a class that has the method:
class Units(i: Int) {
def millis = i
}
implicit def toUnits(i: Int) = new Units(i)
class Specs(s: String) {
def in(thunk: => Unit) = thunk
}
implicit def toSpecs(s: String) = new Specs(s)
See also "Where does Scala looks for Implicits?"
If I'm not mistaken, those pieces of code can be desugared as
within(500.millis)
and
"Testcase description".in({ ... })
This should make it easier to see what's going on.