Last active
December 23, 2025 06:04
-
-
Save ddrpa/ade974993edbf3a50b65f8d7103beec6 to your computer and use it in GitHub Desktop.
提供了一种声明式构造复杂动态 JSON 对象的方法,基于 FasterXML/jackson
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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