Skip to content

Instantly share code, notes, and snippets.

@ddrpa
Last active December 23, 2025 06:04
Show Gist options
  • Select an option

  • Save ddrpa/ade974993edbf3a50b65f8d7103beec6 to your computer and use it in GitHub Desktop.

Select an option

Save ddrpa/ade974993edbf3a50b65f8d7103beec6 to your computer and use it in GitHub Desktop.
提供了一种声明式构造复杂动态 JSON 对象的方法,基于 FasterXML/jackson
package cc.ddrpa.dorian.dsl;
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 java.util.AbstractMap;
import java.util.Collection;
import java.util.Map;
import java.util.function.Supplier;
/**
* 提供了一种声明式构造复杂动态 JSON 对象的方法,基于 FasterXML/jackson,详细说明参考
* <a href=https://yufanonsoftware.me/posts/dsl-for-build-json-with-jackson.html>文章</a>
* 或
* <a href=https://yufanonsoftware.cc/posts/dsl-for-build-json-with-jackson.html>文章(镜像)</a>
*/
public class JacksonDSL {
private static final SKIP SKIP = new SKIP();
private static ObjectMapper mapper = new ObjectMapper();
/**
* 替换为自定义的 ObjectMapper 实例,实现某些特殊转换需求,影响全部 DSL 方法
*
* @param mapper
*/
public static void setObjectMapper(ObjectMapper mapper) {
JacksonDSL.mapper = mapper;
}
/**
* p(key, value) 添加一个键值对成员
*
* @param key 属性名
* @param value 属性值
* @return
*/
public static Map.Entry<String, Object> p(String key, Object value) {
return new AbstractMap.SimpleEntry<>(key, value);
}
/**
* 条件为真时添加成员,否则跳过
*
* @param condition 条件
* @param key 属性名
* @param value 属性值
* @return
*/
public static Map.Entry<String, Object> p(boolean condition, String key, Object value) {
return condition ? p(key, value) : new AbstractMap.SimpleEntry<>(key, SKIP);
}
/**
* 添加一个键值对成员,属性值支持惰性求值
*
* @param key 属性名
* @param supplier 该方法最终计算出属性值
* @return
*/
public static Map.Entry<String, Object> p(String key, Supplier<?> supplier) {
return new AbstractMap.SimpleEntry<>(key, supplier);
}
/**
* 条件添加一个键值对成员,属性值支持惰性求值
*
* @param condition 条件
* @param key 属性名
* @param supplier 该方法最终计算出属性值
* @return
*/
public static Map.Entry<String, Object> p(boolean condition, String key, Supplier<?> supplier) {
return condition ? p(key, supplier) : new AbstractMap.SimpleEntry<>(key, SKIP);
}
/**
* o(p(...), p(...)) 构造一个 ObjectNode 对象
*
* @param entries
* @return
*/
@SafeVarargs
public static ObjectNode o(Map.Entry<String, Object>... entries) {
ObjectNode node = mapper.createObjectNode();
for (Map.Entry<String, Object> e : entries) {
Object v = e.getValue();
// 跳过条件不满足的属性
if (v instanceof SKIP) {
continue;
} else if (v instanceof Supplier<?> supplier) {
// 惰性求值:在此处真正计算值
Object resolved = supplier.get();
node.set(e.getKey(), resolve(resolved));
} else if (v instanceof Map.Entry) {
throw new IllegalArgumentException("Use o() or a(), not nested p().");
} else if (v instanceof ObjectNode on) {
node.set(e.getKey(), on);
} else if (v instanceof ArrayNode an) {
node.set(e.getKey(), an);
} else if (v instanceof Collection<?> collection) {
node.set(e.getKey(), collectionToArrayNode(collection));
} else if (v instanceof JsonNode jn) {
node.set(e.getKey(), jn);
} else if (v instanceof String s) {
node.put(e.getKey(), s);
} else if (v instanceof Integer i) {
node.put(e.getKey(), i);
} else if (v instanceof Long l) {
node.put(e.getKey(), l);
} else if (v instanceof Double d) {
node.put(e.getKey(), d);
} else if (v instanceof Boolean b) {
node.put(e.getKey(), b);
} else if (v == null) {
node.putNull(e.getKey());
} else {
// fallback: convert POJO or Map/List into JsonNode
node.set(e.getKey(), mapper.valueToTree(v));
}
}
return node;
}
/**
* a(o(...), o(...)) 构造一个 ArrayNode 对象
*
* @param values
* @return
*/
public static ArrayNode a(Object... values) {
ArrayNode arr = mapper.createArrayNode();
for (Object v : values) {
if (v instanceof Supplier<?> supplier) {
// 惰性求值:在此处真正计算值
Object resolved = supplier.get();
arr.add(resolve(resolved));
} else if (v instanceof ObjectNode on) {
arr.add(on);
} else if (v instanceof ArrayNode an) {
arr.add(an);
} else if (v instanceof Collection<?> collection) {
arr.add(collectionToArrayNode(collection));
} else if (v instanceof JsonNode jn) {
arr.add(jn);
} else if (v instanceof String s) {
arr.add(s);
} else if (v instanceof Integer i) {
arr.add(i);
} else if (v instanceof Long l) {
arr.add(l);
} else if (v instanceof Double d) {
arr.add(d);
} else if (v instanceof Boolean b) {
arr.add(b);
} else if (v == null) {
arr.addNull();
} else {
arr.add(mapper.valueToTree(v));
}
}
return arr;
}
/**
* 将值解析为 JsonNode
*
* @param v
* @return
*/
private static JsonNode resolve(Object v) {
if (v instanceof ObjectNode on) {
return on;
} else if (v instanceof ArrayNode an) {
return an;
} else if (v instanceof Collection<?> collection) {
return collectionToArrayNode(collection);
} else if (v instanceof JsonNode jn) {
return jn;
} else if (v instanceof Supplier<?> supplier) {
// 支持嵌套的 Supplier
return resolve(supplier.get());
} else if (v instanceof String s) {
return mapper.getNodeFactory().textNode(s);
} else if (v instanceof Integer i) {
return mapper.getNodeFactory().numberNode(i);
} else if (v instanceof Long l) {
return mapper.getNodeFactory().numberNode(l);
} else if (v instanceof Double d) {
return mapper.getNodeFactory().numberNode(d);
} else if (v instanceof Boolean b) {
return mapper.getNodeFactory().booleanNode(b);
} else if (v == null) {
return mapper.getNodeFactory().nullNode();
} else {
return mapper.valueToTree(v);
}
}
/**
* 将 Collection 转换为 ArrayNode
*/
private static ArrayNode collectionToArrayNode(Collection<?> collection) {
ArrayNode arr = mapper.createArrayNode();
for (Object item : collection) {
arr.add(resolve(item));
}
return arr;
}
private JacksonDSL() {
throw new UnsupportedOperationException("JacksonDSL is a utility class and cannot be instantiated.");
}
private static final class SKIP {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment