Commit 4d601907 by Harish Butani

support ReferenceableInstance json serialization

parent e05c8845
...@@ -21,9 +21,11 @@ package org.apache.metadata.storage; ...@@ -21,9 +21,11 @@ package org.apache.metadata.storage;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.apache.metadata.IReferenceableInstance; import org.apache.metadata.IReferenceableInstance;
import org.apache.metadata.IStruct; import org.apache.metadata.IStruct;
import org.apache.metadata.ITypedReferenceableInstance;
import org.apache.metadata.MetadataException; import org.apache.metadata.MetadataException;
import org.apache.metadata.types.FieldMapping;
public class Id implements IReferenceableInstance { public class Id implements ITypedReferenceableInstance {
public final long id; public final long id;
public final String className; public final String className;
...@@ -76,4 +78,9 @@ public class Id implements IReferenceableInstance { ...@@ -76,4 +78,9 @@ public class Id implements IReferenceableInstance {
public void set(String attrName, Object val) throws MetadataException { public void set(String attrName, Object val) throws MetadataException {
throw new MetadataException("Get/Set not supported on an Id object"); throw new MetadataException("Get/Set not supported on an Id object");
} }
@Override
public FieldMapping fieldMapping() {
return null;
}
} }
...@@ -153,8 +153,12 @@ public class ClassType extends HierarchicalType<ClassType, IReferenceableInstanc ...@@ -153,8 +153,12 @@ public class ClassType extends HierarchicalType<ClassType, IReferenceableInstanc
return createInstance(null); return createInstance(null);
} }
public ITypedReferenceableInstance createInstance(Id id) throws MetadataException { public ITypedReferenceableInstance createInstance(String... traitNames) throws MetadataException {
return createInstanceWithTraits(id, null); return createInstance(null, traitNames);
}
public ITypedReferenceableInstance createInstance(Id id, String... traitNames) throws MetadataException {
return createInstanceWithTraits(id, null, traitNames);
} }
public ITypedReferenceableInstance createInstanceWithTraits(Id id, Referenceable r, String... traitNames) public ITypedReferenceableInstance createInstanceWithTraits(Id id, Referenceable r, String... traitNames)
......
...@@ -18,9 +18,11 @@ ...@@ -18,9 +18,11 @@
package org.apache.metadata.json package org.apache.metadata.json
import org.apache.jute.compiler.JLong
import org.apache.metadata.types.DataTypes.{MapType, TypeCategory, ArrayType} import org.apache.metadata.types.DataTypes.{MapType, TypeCategory, ArrayType}
import org.apache.metadata.{ITypedStruct, IStruct, MetadataException, MetadataService} import org.apache.metadata._
import org.apache.metadata.types._ import org.apache.metadata.types._
import org.apache.metadata.storage.Id
import org.json4s.JsonAST.JInt import org.json4s.JsonAST.JInt
import org.json4s._ import org.json4s._
import org.json4s.native.Serialization.{read, write => swrite} import org.json4s.native.Serialization.{read, write => swrite}
...@@ -44,8 +46,127 @@ class BigIntegerSerializer extends CustomSerializer[java.math.BigInteger](format ...@@ -44,8 +46,127 @@ class BigIntegerSerializer extends CustomSerializer[java.math.BigInteger](format
} }
)) ))
class IdSerializer extends CustomSerializer[Id](format => ( {
case JObject(JField("id", JInt(id)) ::
JField(Serialization.STRUCT_TYPE_FIELD_NAME, JString(className)) ::
JField("version", JInt(version)) :: Nil) => new Id(id.toLong, version.toInt, className)
}, {
case id: Id => JObject(JField("id", JInt(id.id)),
JField(Serialization.STRUCT_TYPE_FIELD_NAME, JString(id.className)),
JField("version", JInt(id.version)))
}
))
class TypedStructSerializer extends Serializer[ITypedStruct] { class TypedStructSerializer extends Serializer[ITypedStruct] {
def deserialize(implicit format: Formats) = {
case (TypeInfo(clazz, ptype), json) if classOf[ITypedStruct].isAssignableFrom(clazz) => json match {
case JObject(fs) =>
val(typ, fields) = fs.partition(f => f._1 == Serialization.STRUCT_TYPE_FIELD_NAME)
val typName = typ(0)._2.asInstanceOf[JString].s
val sT = MetadataService.getCurrentTypeSystem().getDataType(
classOf[IConstructableType[IStruct, ITypedStruct]], typName).asInstanceOf[IConstructableType[IStruct, ITypedStruct]]
val s = sT.createInstance()
Serialization.deserializeFields(sT, s, fields)
s
case x => throw new MappingException("Can't convert " + x + " to TypedStruct")
}
}
/**
* Implicit conversion from `java.math.BigInteger` to `scala.BigInt`.
* match the builtin conversion for BigDecimal.
* See https://groups.google.com/forum/#!topic/scala-language/AFUamvxu68Q
*/
//implicit def javaBigInteger2bigInt(x: java.math.BigInteger): BigInt = new BigInt(x)
def serialize(implicit format: Formats) = {
case e: ITypedStruct =>
val fields = Serialization.serializeFields(e)
JObject(JField(Serialization.STRUCT_TYPE_FIELD_NAME, JString(e.getTypeName)) :: fields)
}
}
class TypedReferenceableInstanceSerializer extends Serializer[ITypedReferenceableInstance] {
def deserialize(implicit format: Formats) = {
case (TypeInfo(clazz, ptype), json) if classOf[ITypedReferenceableInstance].isAssignableFrom(clazz) => json match {
case JObject(JField("id", JInt(id)) ::
JField(Serialization.STRUCT_TYPE_FIELD_NAME, JString(className)) ::
JField("version", JInt(version)) :: Nil) => new Id(id.toLong, version.toInt, className)
case JObject(fs) =>
var typField : Option[JField] = None
var idField : Option[JField] = None
var traitsField : Option[JField] = None
var fields : List[JField] = Nil
fs.foreach {f : JField => f._1 match {
case Serialization.STRUCT_TYPE_FIELD_NAME => typField = Some(f)
case Serialization.ID_TYPE_FIELD_NAME => idField = Some(f)
case Serialization.TRAIT_TYPE_FIELD_NAME => traitsField = Some(f)
case _ => fields = fields :+ f
}
}
var traitNames : List[String] = Nil
traitsField.map { t =>
val tObj :JObject = t._2.asInstanceOf[JObject]
tObj.obj.foreach { oTrait =>
val tName: String = oTrait._1
traitNames = traitNames :+ tName
}
}
val typName = typField.get._2.asInstanceOf[JString].s
val sT = MetadataService.getCurrentTypeSystem().getDataType(
classOf[ClassType], typName).asInstanceOf[ClassType]
val id = Serialization.deserializeId(idField.get._2)
val s = sT.createInstance(id, traitNames:_*)
Serialization.deserializeFields(sT, s, fields)
traitsField.map { t =>
val tObj :JObject = t._2.asInstanceOf[JObject]
tObj.obj.foreach { oTrait =>
val tName : String = oTrait._1
val traitJObj : JObject = oTrait._2.asInstanceOf[JObject]
val traitObj = s.getTrait(tName).asInstanceOf[ITypedStruct]
val tT = MetadataService.getCurrentTypeSystem().getDataType(
classOf[TraitType], traitObj.getTypeName).asInstanceOf[TraitType]
val(tTyp, tFields) = traitJObj.obj.partition(f => f._1 == Serialization.STRUCT_TYPE_FIELD_NAME)
Serialization.deserializeFields(tT, traitObj, tFields)
}
}
s
case x => throw new MappingException("Can't convert " + x + " to TypedStruct")
}
}
def serialize(implicit format: Formats) = {
case id : Id => Serialization.serializeId(id)
case e: ITypedReferenceableInstance =>
val idJ = JField(Serialization.ID_TYPE_FIELD_NAME, Serialization.serializeId(e.getId))
var fields = Serialization.serializeFields(e)
val traitsJ : List[JField] = e.getTraits.map( tName => JField(tName,Extraction.decompose(e.getTrait(tName)))).toList
fields = idJ :: fields
if ( traitsJ.size > 0 ) {
fields = fields :+ JField(Serialization.TRAIT_TYPE_FIELD_NAME, JObject(traitsJ:_*))
}
JObject(JField(Serialization.STRUCT_TYPE_FIELD_NAME, JString(e.getTypeName)) :: fields)
}
}
object Serialization {
val STRUCT_TYPE_FIELD_NAME = "$typeName$"
val ID_TYPE_FIELD_NAME = "$id$"
val TRAIT_TYPE_FIELD_NAME = "$traits$"
def extractList(lT : ArrayType, value : JArray)(implicit format: Formats) : Any = { def extractList(lT : ArrayType, value : JArray)(implicit format: Formats) : Any = {
val dT = lT.getElemType val dT = lT.getElemType
value.arr.map(extract(dT, _)).asJava value.arr.map(extract(dT, _)).asJava
...@@ -67,19 +188,34 @@ class TypedStructSerializer extends Serializer[ITypedStruct] { ...@@ -67,19 +188,34 @@ class TypedStructSerializer extends Serializer[ITypedStruct] {
case value : JArray => extractList(dT.asInstanceOf[ArrayType], value.asInstanceOf[JArray]) case value : JArray => extractList(dT.asInstanceOf[ArrayType], value.asInstanceOf[JArray])
case value : JObject if dT.getTypeCategory eq TypeCategory.MAP => case value : JObject if dT.getTypeCategory eq TypeCategory.MAP =>
extractMap(dT.asInstanceOf[MapType], value.asInstanceOf[JObject]) extractMap(dT.asInstanceOf[MapType], value.asInstanceOf[JObject])
case value : JObject => case value : JObject if ((dT.getTypeCategory eq TypeCategory.STRUCT) || (dT.getTypeCategory eq TypeCategory.TRAIT)) =>
Extraction.extract[ITypedStruct](value) Extraction.extract[ITypedStruct](value)
case value : JObject =>
Extraction.extract[ITypedReferenceableInstance](value)
} }
def deserialize(implicit format: Formats) = { def serializeId(id : Id) = JObject(JField("id", JInt(id.id)),
case (TypeInfo(clazz, ptype), json) if classOf[ITypedStruct].isAssignableFrom(clazz) => json match { JField(Serialization.STRUCT_TYPE_FIELD_NAME, JString(id.className)),
case JObject(fs) => JField("version", JInt(id.version)))
val(typ, fields) = fs.partition(f => f._1 == Serialization.STRUCT_TYPE_FIELD_NAME)
val typName = typ(0)._2.asInstanceOf[JString].s def serializeFields(e : ITypedInstance)(implicit format: Formats) = e.fieldMapping.fields.map {
val sT = MetadataService.getCurrentTypeSystem().getDataType( case (fName, info) => {
classOf[IConstructableType[IStruct, ITypedStruct]], typName).asInstanceOf[IConstructableType[IStruct, ITypedStruct]] var v = e.get(fName)
val s = sT.createInstance() if ( v != null && (info.dataType().getTypeCategory eq TypeCategory.MAP) ) {
fields.foreach { f => v = v.asInstanceOf[java.util.Map[_,_]].toMap
}
if ( v != null && (info.dataType().getTypeCategory eq TypeCategory.CLASS) && !info.isComposite ) {
v = v.asInstanceOf[IReferenceableInstance].getId
}
JField(fName, Extraction.decompose(v))
}
}.toList.map(_.asInstanceOf[JField])
def deserializeFields[T <: ITypedInstance](sT : IConstructableType[_, T],
s : T, fields : List[JField] )(implicit format: Formats)
= fields.foreach { f =>
val fName = f._1 val fName = f._1
val fInfo = sT.fieldMapping.fields(fName) val fInfo = sT.fieldMapping.fields(fName)
if ( fInfo != null ) { if ( fInfo != null ) {
...@@ -93,37 +229,15 @@ class TypedStructSerializer extends Serializer[ITypedStruct] { ...@@ -93,37 +229,15 @@ class TypedStructSerializer extends Serializer[ITypedStruct] {
case x => x case x => x
} }
} }
s.set(fName, extract(fInfo.dataType(), v)) s.set(fName, Serialization.extract(fInfo.dataType(), v))
} }
} }
s
case x => throw new MappingException("Can't convert " + x + " to TypedStruct")
}
def deserializeId(value : JValue)(implicit format: Formats) = value match {
case JObject(JField("id", JInt(id)) ::
JField(Serialization.STRUCT_TYPE_FIELD_NAME, JString(className)) ::
JField("version", JInt(version)) :: Nil) => new Id(id.toLong, version.toInt, className)
} }
/**
* Implicit conversion from `java.math.BigInteger` to `scala.BigInt`.
* match the builtin conversion for BigDecimal.
* See https://groups.google.com/forum/#!topic/scala-language/AFUamvxu68Q
*/
//implicit def javaBigInteger2bigInt(x: java.math.BigInteger): BigInt = new BigInt(x)
def serialize(implicit format: Formats) = {
case e: ITypedStruct =>
val fields = e.fieldMapping.fields.map {
case (fName, info) => {
var v = e.get(fName)
if ( v != null && (info.dataType().getTypeCategory eq TypeCategory.MAP) ) {
v = v.asInstanceOf[java.util.Map[_,_]].toMap
}
JField(fName, Extraction.decompose(v))
}
}.toList.map(_.asInstanceOf[JField])
JObject(JField(Serialization.STRUCT_TYPE_FIELD_NAME, JString(e.getTypeName)) :: fields)
}
}
object Serialization {
val STRUCT_TYPE_FIELD_NAME = "$typeName$"
} }
...@@ -19,9 +19,8 @@ ...@@ -19,9 +19,8 @@
package org.apache.metadata.json package org.apache.metadata.json
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import org.apache.metadata.{ITypedStruct, Struct, BaseTest} import org.apache.metadata._
import org.apache.metadata.storage.StructInstance import org.apache.metadata.storage.{ReferenceableInstance, StructInstance}
import org.apache.metadata.storage.StructInstance
import org.apache.metadata.types._ import org.apache.metadata.types._
import org.json4s.NoTypeHints import org.json4s.NoTypeHints
import org.junit.Before import org.junit.Before
...@@ -29,7 +28,7 @@ import org.junit.Test ...@@ -29,7 +28,7 @@ import org.junit.Test
import org.junit.Assert import org.junit.Assert
import org.json4s._ import org.json4s._
import org.json4s.native.Serialization.{read, write => swrite} import org.json4s.native.Serialization.{write => swrite, _}
import org.json4s.native.JsonMethods._ import org.json4s.native.JsonMethods._
class SerializationTest extends BaseTest { class SerializationTest extends BaseTest {
...@@ -126,4 +125,59 @@ class SerializationTest extends BaseTest { ...@@ -126,4 +125,59 @@ class SerializationTest extends BaseTest {
// Typed Struct read from string: // Typed Struct read from string:
Assert.assertEquals(ts1.toString, "{\n\td : \t1\n\tb : \ttrue\n\tc : \t1\n\ta : \t1\n\tA.B.D.b : \ttrue\n\tA.B.D.c : \t2\n\tA.B.D.d : \t2\n\tA.C.D.a : \t3\n\tA.C.D.b : \tfalse\n\tA.C.D.c : \t3\n\tA.C.D.d : \t3\n}") Assert.assertEquals(ts1.toString, "{\n\td : \t1\n\tb : \ttrue\n\tc : \t1\n\ta : \t1\n\tA.B.D.b : \ttrue\n\tA.B.D.c : \t2\n\tA.B.D.d : \t2\n\tA.C.D.a : \t3\n\tA.C.D.b : \tfalse\n\tA.C.D.c : \t3\n\tA.C.D.d : \t3\n}")
} }
@Test def testClass {
val ts: TypeSystem = ms.getTypeSystem
val deptTypeDef: HierarchicalTypeDefinition[ClassType] = createClassTypeDef("Department",
ImmutableList.of[String],
BaseTest.createRequiredAttrDef("name", DataTypes.STRING_TYPE),
new AttributeDefinition("employees", String.format("array<%s>", "Person"),
Multiplicity.COLLECTION, true, "department"))
val personTypeDef: HierarchicalTypeDefinition[ClassType] = createClassTypeDef("Person",
ImmutableList.of[String],
BaseTest.createRequiredAttrDef("name", DataTypes.STRING_TYPE),
new AttributeDefinition("department", "Department", Multiplicity.REQUIRED, false, "employees"),
new AttributeDefinition("manager", "Manager", Multiplicity.OPTIONAL, false, "subordinates"))
val managerTypeDef: HierarchicalTypeDefinition[ClassType] = createClassTypeDef("Manager",
ImmutableList.of[String]("Person"),
new AttributeDefinition("subordinates", String.format("array<%s>", "Person"),
Multiplicity.COLLECTION, false, "manager"))
val securityClearanceTypeDef: HierarchicalTypeDefinition[TraitType] = createTraitTypeDef("SecurityClearance",
ImmutableList.of[String],
BaseTest.createRequiredAttrDef("level", DataTypes.INT_TYPE))
ts.defineTypes(ImmutableList.of[StructTypeDefinition],
ImmutableList.of[HierarchicalTypeDefinition[TraitType]](securityClearanceTypeDef),
ImmutableList.of[HierarchicalTypeDefinition[ClassType]](deptTypeDef, personTypeDef, managerTypeDef)
)
val hrDept: Referenceable = new Referenceable("Department")
val john: Referenceable = new Referenceable("Person")
val jane: Referenceable = new Referenceable("Manager", "SecurityClearance")
hrDept.set("name", "hr")
john.set("name", "John")
john.set("department", hrDept)
jane.set("name", "Jane")
jane.set("department", hrDept)
john.set("manager", jane)
hrDept.set("employees", ImmutableList.of[Referenceable](john, jane))
jane.set("subordinates", ImmutableList.of[Referenceable](john))
jane.getTrait("SecurityClearance").set("level", 1)
val deptType: ClassType = ts.getDataType(classOf[ClassType], "Department")
val hrDept2: ITypedReferenceableInstance = deptType.convert(hrDept, Multiplicity.REQUIRED)
println(s"HR Dept Object Graph:\n${hrDept2}\n")
implicit val formats = org.json4s.native.Serialization.formats(NoTypeHints) + new TypedStructSerializer +
new TypedReferenceableInstanceSerializer + new BigDecimalSerializer + new BigIntegerSerializer
val ser = swrite(hrDept2)
println(s"HR Dept JSON:\n${pretty(render(parse(ser)))}\n" )
println(s"HR Dept Object Graph read from JSON:${read[ReferenceableInstance](ser)}\n")
}
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment