-
Notifications
You must be signed in to change notification settings - Fork 0
CustomContext
Probably not! Usually, importing Salat's own global context will do the trick:
import com.novus.salat.global._
The purpose of a custom context is to globally override some default in Salat Context to suit your own use case:
- register global key overrides
- register per-class key overrides
- customise type hints
- add addition classloaders or replace the default classloader
- customise enum serialization
- customise
BigDecimalprecision with your ownjava.math.MathContext
- First, define a custom context as
implicit val ctx = new Context { /* custom behaviour */ }in a package object -
Stop importing
com.novus.salat.global._ - Import your own custom context everywhere instead.
Please be careful: accidentally mixing implicit contexts can probably will cause severe tire damage.
In the example below, use your custom context by replacing import com.novus.salat.global._ with import foo.custom_context._
everywhere you need to supply an implicit ctx val:
package foo
import com.novus.salat._
package object custom_context {
implicit val ctx = new Context {
val name = "Custom Context"
// some overrides or custom behavior
}
}See type hinting for more information before you change anything.
Defining a context that always uses type hints, but changes the type hint field name from _typeHint to _t :
package com.novus.salat.test
import com.novus.salat._
import com.mongodb.casbah.Imports._
package object custom_type_hint {
val CustomTypeHint = "_t"
implicit val ctx = new Context {
val name = "TestContext-Always"
override val typeHintStrategy = TypeHintStrategy(when = TypeHintFrequency.Always, typeHint = CustomTypeHint)
}
}Then, in your code, import your own custom context instead of Salat's global context:
case class Alpha(x: String)
scala> import com.novus.salat.test.custom_type_hint._
import com.novus.salat.test.custom_type_hint._
scala> val a = Alpha(x = "Hello world")
a: com.novus.salat.test.model.Alpha = Alpha(Hello world)
scala> val dbo = grater[Alpha].asDBObject(a)
dbo: com.mongodb.casbah.Imports.DBObject = { "_t" : "com.novus.salat.test.model.Alpha" , "x" : "Hello world"}
scala> val a_* = grater[Alpha].asObject(dbo)
a_*: com.novus.salat.test.model.Alpha = Alpha(Hello world)
scala> a == a_*
res0: Boolean = trueLet's say that you don't want to keep repeating yourself:
@Key("_id") id: ObjectId = new ObjectId
In that case, you can define a global key override in a custom context - the example below will remap id to _id :
package com.novus.salat.test
import com.novus.salat._
import com.mongodb.casbah.Imports._
package object global_key_overrides {
implicit val ctx = new Context {
val name = "TestContext-GlobalKeyOverrides"
}
// now remap away to your heart's content
ctx.registerGlobalKeyOverride(remapThis = "id", toThisInstead = "_id")
}Then, in your code, import your own custom context instead of Salat's global context:
case class Beta(id: ObjectId = new ObjectId, x: String)
scala> import com.novus.salat.test.global_key_overrides._
import com.novus.salat.test.global_key_overrides._
scala> val b = Beta(x = "Hello world")
b: com.novus.salat.test.model.Beta = Beta(4dac7fe775e101ed63792313,Hello world)
scala> val dbo = grater[Beta].asDBObject(b)
dbo: com.mongodb.casbah.Imports.DBObject = { "_typeHint" : "com.novus.salat.test.model.Beta" ,
"_id" : { "$oid" : "4dac7fe775e101ed63792313"},
"x" : "Hello world"}
scala> val b_* = grater[Beta].asObject(dbo)
b_*: com.novus.salat.test.model.Beta = Beta(4dac7fe775e101ed63792313,Hello world)
scala> b == b_*
res0: Boolean = trueIf you don't want to globally remap all instances of a field name, you can limit the key remapping per-class.
For example, given these model classes:
import com.novus.salat.test.model
case class Rhoda(consumed: Option[String] = None)
case class Rhoda3(consumed: Option[String] = None)This custom context will remap consumed for Rhoda but not for Rhoda3:
package com.novus.salat.test
import com.novus.salat._
import com.novus.salat.test.model.Rhoda
package object per_class_key_remapping {
implicit val ctx = new Context {
val name = "TestContext-PerClassKeyRemapping"
}
ctx.registerPerClassKeyOverride(classOf[Rhoda], remapThis = "consumed", toThisInstead = "fire")
}When serializing and deserializing instances of Rhoda, consumed will be remapped to fire. However, when
serializing and deserializing instances of Rhoda3, consumed will not be remapped because the Context limits
the key remapping by class.
scala> import com.novus.salat._
import com.novus.salat._
scala> import com.novus.salat.test.model._
import com.novus.salat.test.model._
scala> import com.novus.salat.test.per_class_key_remapping._
import com.novus.salat.test.per_class_key_remapping._
scala> import com.mongodb.casbah.Imports._
import com.mongodb.casbah.Imports._
scala> val rhoda = Rhoda(consumed = Some("indeed"))
rhoda: com.novus.salat.test.model.Rhoda = Rhoda(Some(indeed))
scala> val dbo: MongoDBObject = grater[Rhoda].asDBObject(rhoda)
44 [run-main] INFO com.novus.salat.test.per_class_key_remapping.package$$anon$6 - perClassKeyOverrides: context=Some(TestContext-PerClassKeyRemapping) will remap key='consumed' to 'fire' for all instance of clazz='com.novus.salat.test.model.Rhoda'
dbo: com.mongodb.casbah.Imports.MongoDBObject = Map((_typeHint,com.novus.salat.test.model.Rhoda), (fire,indeed))
scala> grater[Rhoda].asObject(dbo)
res1: com.novus.salat.test.model.Rhoda = Rhoda(Some(indeed))
scala> val rhoda3 = Rhoda3(consumed = Some("indeed"))
rhoda3: com.novus.salat.test.model.Rhoda3 = Rhoda3(Some(indeed))
scala> val dbo2: MongoDBObject = grater[Rhoda3].asDBObject(rhoda3)
dbo2: com.mongodb.casbah.Imports.MongoDBObject = Map((_typeHint,com.novus.salat.test.model.Rhoda3), (consumed,indeed))
scala> grater[Rhoda3].asObject(dbo2)
res2: com.novus.salat.test.model.Rhoda3 = Rhoda3(Some(indeed))You can register multiple classloaders in addition to the default classloader:
package foo
import com.novus.salat._
package object custom_classloader {
implicit val ctx = new Context {
val name = "Custom_Classloader"
}
ctx.registerClassLoader(MyCustomClassloader)
}Or replace the default classloader by overriding the classLoaders param in your own class:
package foo
import com.novus.salat._
package object custom_classloader {
implicit val ctx = new Context {
val name = "Custom_Classloader"
classLoaders = Seq(MyCustomClassloader)
}
}Salat's default enum handling stategy in Context is by value.
// sets up a default enum strategy of using toString to serialize/deserialize enums
val defaultEnumStrategy = EnumStrategy.BY_VALUEAssuming you need to change it, create a custom context like this:
package com.novus.salat.test
import com.novus.salat._
import com.novus.salat.annotations.raw._
package object handle_my_enums_diffrent {
implicit val ctx = new Context {
val name = "CustomEnumStrategy"
override val defaultEnumStrategy = EnumStrategy.BY_ID
}
}The default precision for BigDecimal in Context is
val mathCtx = new MathContext(17, RoundingMode.HALF_UP)
Assuming you need to change it, create a custom context like this:
package com.novus.salat.test
import com.novus.salat._
import java.math.{RoundingMode, MathContext}
package object moar_precision {
implicit val ctx = new Context {
val name = "MorePreciseContext"
override val mathCtx = new MathContext(17, RoundingMode.HALF_UP)
}
}Careful with that axe, Eugene. You need to make sure you are using your custom context instead of the global context
everywhere.