macro package CJson.jsonmacro
import std.ast.*
internal import std.collection.*
/*
* Serializer of json for target class
*/
class ClassJsonDeserilizer <: ClassProcessor {
public init(globalConfig: GlobalConfig) {
super(globalConfig)
}
/*
* Makes fromJson() func of target class
*
* @param classIdentity_Tk the Identifier token for target class
* @param var_Tk_List the list for memeber var token of the target class
* @return Tokens the fromJson() func of target class represented in tokens
*/
public func makeFromJsonFunc(classIdentity_Tk: Token, var_Tk_List: ArrayList<VarDecl>): Tokens {
var jsonOjbectToken = Token(TokenKind.IDENTIFIER, "jsonObject")
return quote(
public static func fromJson(json: String): $classIdentity_Tk {
return $classIdentity_Tk.fromJsonValue(JsonValue.fromStr(json))
}
public static func fromJsonValue(json: JsonValue): $classIdentity_Tk {
var ret = $classIdentity_Tk()
var $jsonOjbectToken = json.asObject()
$(parseJsonFields(var_Tk_List, jsonOjbectToken))
return ret
}
)
}
private func parseJsonFields(filedInfoList: ArrayList<VarDecl>, jsonOjbect_Tk: Token): Tokens {
var fieldParseToken = Tokens()
for(fieldInfo in filedInfoList) {
fieldParseToken = fieldParseToken + parseJsonFiled(fieldInfo, jsonOjbect_Tk)
}
return fieldParseToken
}
private func parseJsonFiled(var_Tk: VarDecl, jsonOjbectVar_Tk: Token): Tokens {
let varInfo = getVarInfo(var_Tk)
var mappedNameValue = getMappedName(varInfo.name)
let getFieldCore_Tk = parseJsonFiledCore(var_Tk, jsonOjbectVar_Tk, varInfo)
return quote(
if($jsonOjbectVar_Tk.getFields().contains($mappedNameValue)) {
$getFieldCore_Tk
}
)
}
private func parseJsonFiledCore(var_Tk: VarDecl, jsonOjbectVar_Tk: Token, varInfo: VarInfo): Tokens {
var mappedNameValue = getMappedName(varInfo.name)
var nodeJson_Tk = quote($jsonOjbectVar_Tk.get($mappedNameValue).getOrThrow())
var valueReveiver_Tk = quote(ret.$(varInfo.identifier))
if (globalConfig.propAdaptorFactry.hasAdaptor(varInfo.identifier.toTokens().toString())) {
//1. custom property with @JsonCust[Serializer]
return getCustProp(nodeJson_Tk, varInfo)
} else if (varInfo.typeName == "Option") {
//2. Option<T> property
return getOptionPorp(nodeJson_Tk, varInfo)
} else if (varInfo.typeArguments.size > 0) {
//3. Generic property such as ArrayList, HashSet, HashMap
getGenericProp(nodeJson_Tk, varInfo)
} else {
//4. Primitive property
getProp(nodeJson_Tk, varInfo)
}
}
private func getCustProp(nodeJson_Tk: Tokens, varInfo: VarInfo): Tokens {
let serializer_Tk = Token(TokenKind.IDENTIFIER,
globalConfig.propAdaptorFactry.getAdaptor(varInfo.identifier.toTokens().toString()))
return quote(
ret.$(varInfo.identifier) = $serializer_Tk.fromJsonValue($nodeJson_Tk)
)
}
private func getOptionPorp(nodeJson_Tk: Tokens, varInfo: VarInfo): Tokens {
if (varInfo.typeArguments.size == 0) {
throw Exception("Option generic type not provided")
}
let optionType = varInfo.typeArguments[0]
return quote(
if ($nodeJson_Tk is JsonNull) {
ret.$(varInfo.identifier) = Option<$optionType>.None
} else {
try {
ret.$(varInfo.identifier) = $optionType.fromJsonValue($nodeJson_Tk)
} catch(exp: Exception) {
ret.$(varInfo.identifier) = Option<$optionType>.None
}
}
)
}
private func getGenericProp(nodeJson_Tk: Tokens, varInfo: VarInfo): Tokens {
var serializer_Tk = Token(TokenKind.IDENTIFIER, varInfo.typeName)
var genericType_Tk = varInfo.typeArguments.flattenWithComma()
return quote(
ret.$(varInfo.identifier) = $serializer_Tk<$genericType_Tk>.fromJsonValue($nodeJson_Tk)
)
}
private func getProp(nodeJson_Tk: Tokens, varInfo: VarInfo): Tokens {
var serializer_Tk = Token(TokenKind.IDENTIFIER, varInfo.typeName)
return quote(
ret.$(varInfo.identifier) = $serializer_Tk.fromJsonValue($nodeJson_Tk)
)
}
}