FXXXXXXXXXCK!!!
DynamoDBに入ってるJsonオブジェクトをJavaで扱えるようにしたいだけなのに
これが調べても調べても「ちゃんとエンティティ用意すればマッピングしてくれるよ!」って方法しか出てこないんですよ。それDynamoじゃなくてよくね??何のためにKVS使ってると思ってるのさ。
Typeを調べる方法すらない
DynamoDBから引っぱってきたデータは AttributeValue
(正確には Map<String, AttributeValue>
) という型に入っているんですが、 Javadoc を見るとわかる通り 値の型を調べる方法が無い んですよ。こいつを直接 toString
してあげるとDynamoDBでおなじみの N
とか S
とか入ってるのが見えるんですけど、 なぜかそいつを参照できるメソッドが無い という始末。
(しかも少し前のバージョンでは hasXs()
系のメソッドや hasM()
すら無かったからリストがなんのリストなのか直接調べる方法がマジで無かったし Map
だった場合は本当にどうしようもなかった)
一応 s()
とか n()
とかで規定外の型を取り出そうとすると null
が返ってくるので、虱潰しに調べるという方法も無くはなかったんですが、 l()
が無条件で空リストを返してくる というとんでもない仕様だったのでなんかもうなんかもう
とりあえず書き殴ったコード
目下必要なところだけ書いたけどたりないものだらけだからマジで公式で出して……ほんと……
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
public class DynamoDbUtil {
private class Type{
static final String nameString = "string";
static final String nameNumeric = "numeric";
static final String nameBoolean = "boolean";
static final String nameArray = "array";
static final String nameMap = "map";
}
static public Boolean isString (AttributeValue a) {
return (a.s() != null);
}
static public Boolean isNumeric (AttributeValue a) {
return (a.n() != null);
}
static public Boolean isBoolean (AttributeValue a) {
return (a.bool() != null);
}
static public Boolean isBinary (AttributeValue a) {
return (a.b() != null);
}
static public Boolean isNumericList (AttributeValue a) {
return a.hasNs();
}
static public Boolean isStringList (AttributeValue a) {
return a.hasSs();
}
static public Boolean isAnyList (AttributeValue a) {
return a.hasL();
}
static public Boolean isMap (AttributeValue a) {
return a.hasM();
}
static public List<Double> toNumberList (List<AttributeValue> as){
if(as.get(0).n() == null){
throw new RuntimeException("its not number");
}
List<Double> ds = as.stream().map(a -> Double.parseDouble(a.n())).collect(Collectors.toList());
return ds;
}
static public List<String> toStringList(List<AttributeValue> as){
if(as.get(0).s() == null){
throw new RuntimeException("its not string");
}
List<String> ss = as.stream().map(a -> a.s()).collect(Collectors.toList());
return ss;
}
static public String toString (AttributeValue a) {
if(isString(a)){
return a.s();
}
if(isNumeric(a)){
return a.n();
}
if(isBoolean(a)){
return Boolean.toString(a.bool());
}
if(isNumericList(a)){
return toNumberList(a.l()).toString();
}
if (isStringList(a)){
return toStringList(a.l()).toString();
}
throw new RuntimeException("inviled value type");
}
static public String type(AttributeValue a){
if(isNumericList(a) || isStringList(a) || isAnyList(a)){
return Type.nameArray;
}
if(isBoolean(a)){
return Type.nameBoolean;
}
if(isNumeric(a)){
return Type.nameNumeric;
}
if(isString(a)){
return Type.nameString;
}
if(isMap(a)){
return Type.nameMap;
}
throw new RuntimeException("inviled value type");
}
static public JsonNode toJsonNode(List<Map<String, AttributeValue>> is){
ObjectMapper mapper = new ObjectMapper();
ArrayNode array = mapper.createArrayNode();
is.forEach((i) ->{
array.add(toJsonNode(i));
});
return array;
}
static public JsonNode toJsonNode(Map<String, AttributeValue> i){
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.createObjectNode();
i.forEach((k, a) -> {
switch(type(a)){
case Type.nameArray:
node.set(k, toJsonNode_AS(a.l()));
break;
case Type.nameBoolean:
node.put(k, a.bool());
break;
case Type.nameNumeric:
node.put(k, Integer.parseInt(a.n()));
break;
case Type.nameMap:
node.set(k, toJsonNode(a.m()));
default:
node.put(k, a.s());
}
});
return node;
}
static public JsonNode toJsonNode_AS(List<AttributeValue> as){
ObjectMapper mapper = new ObjectMapper();
ArrayNode array = mapper.createArrayNode();
as.forEach((a) -> {
logger.debug(type(a));
switch(type(a)){
case Type.nameArray:
array.add(toJsonNode_AS(a.l()));
break;
case Type.nameBoolean:
array.add(a.bool());
break;
case Type.nameNumeric:
array.add(Integer.parseInt(a.n()));
break;
case Type.nameMap:
array.add(toJsonNode(a.m()));
default:
array.add(a.s());
}
});
return array;
}
}