1 package top.infra.jackson2;
2
3 import static java.util.stream.Collectors.toList;
4
5 import com.fasterxml.jackson.core.JsonProcessingException;
6 import com.fasterxml.jackson.core.type.TypeReference;
7 import com.fasterxml.jackson.databind.ObjectMapper;
8
9 import lombok.extern.slf4j.Slf4j;
10
11 import org.springframework.beans.BeanUtils;
12 import org.springframework.core.OrderComparator;
13
14 import java.io.IOException;
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.Set;
18 import java.util.function.Function;
19
20 import top.infra.common.ClassUtils;
21
22
23
24
25 @Slf4j
26 public abstract class Jackson2Utils {
27
28 private static Boolean JACKSON2_PRESENT;
29
30 static {
31 JACKSON2_PRESENT = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper")
32 && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator");
33 }
34
35 private Jackson2Utils() {
36 }
37
38
39
40
41
42
43
44
45 public static ObjectMapper customize(final Jackson2Properties properties, final ObjectMapper objectMapper) {
46 for (final Jackson2MapperCustomizer customizer : jackson2MapperCustomizers()) {
47 log.info("customize objectMapper: '{}' using: '{}'.", objectMapper, customizer);
48 customizer.customize(properties, objectMapper);
49 }
50 return objectMapper;
51 }
52
53 public static <T> Function<String, T> fromJson(
54 final ObjectMapper objectMapper,
55 final TypeReference<T> typeReference
56 ) {
57 return string -> {
58 try {
59 return objectMapper.readValue(string, typeReference);
60 } catch (final IOException wrapped) {
61 throw new RuntimeJsonProcessingException("error read from JSON.", wrapped);
62 }
63 };
64 }
65
66
67
68
69
70
71
72
73
74 public static <T> Function<String, T> fromJson(
75 final ObjectMapper objectMapper,
76 final Class<T> type
77 ) {
78 return string -> {
79 try {
80 return objectMapper.readValue(string, type);
81 } catch (final IOException wrapped) {
82 throw new RuntimeJsonProcessingException("error read from JSON.", wrapped);
83 }
84 };
85 }
86
87 public static Boolean isJackson2Present() {
88 return Jackson2Utils.JACKSON2_PRESENT;
89 }
90
91 static List<Jackson2MapperCustomizer> jackson2MapperCustomizers() {
92 final List<Jackson2MapperCustomizerFactory> factories = jackson2MapperCustomizerFactories();
93
94 return factories.stream()
95 .map(factory -> factory.getObject().orElse(null))
96 .filter(Objects::nonNull)
97 .sorted(OrderComparator.INSTANCE)
98 .collect(toList());
99 }
100
101 static List<Jackson2MapperCustomizerFactory> jackson2MapperCustomizerFactories() {
102 final String basePackage = Jackson2Utils.class.getName().split("\\.")[0];
103
104 final Set<Class<Jackson2MapperCustomizerFactory>> classes = ClassUtils.FileAndClasspathUtils.scan(basePackage,
105 new ClassUtils.FileAndClasspathUtils.AssignableFilter(Jackson2MapperCustomizerFactory.class, false, false));
106
107 log.info("found {} jackson2MapperCustomizerFactories.", classes.size());
108
109 return classes.stream().map(BeanUtils::instantiate).collect(toList());
110 }
111
112
113
114
115
116
117
118
119 public static <T> Function<T, String> toJson(final ObjectMapper objectMapper) {
120 return object -> {
121 try {
122 if (object != null) {
123 return objectMapper.writeValueAsString(object);
124 } else {
125 return "";
126 }
127 } catch (final JsonProcessingException wrapped) {
128 throw new RuntimeJsonProcessingException("error serialize to JSON.", wrapped);
129 }
130 };
131 }
132
133 public static <T> String toJson(final ObjectMapper objectMapper, final T item) {
134 return toJson(objectMapper).apply(item);
135 }
136
137 public static class RuntimeJsonProcessingException extends RuntimeException {
138
139 private static final long serialVersionUID = 1L;
140
141 public RuntimeJsonProcessingException(final String message, final Throwable cause) {
142 super(message, cause);
143 }
144 }
145 }