mirror of
https://gitee.com/dromara/hutool.git
synced 2026-02-09 09:16:26 +08:00
Compare commits
5 Commits
8eff63bc9f
...
90de2c4d59
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90de2c4d59 | ||
|
|
b7035dd4b4 | ||
|
|
eac5024887 | ||
|
|
924357730e | ||
|
|
4db6ac1f34 |
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.core.io.stream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* <p>This class is used to write a stream of chars as a stream of
|
||||
* bytes using the UTF8 encoding. It assumes that the underlying
|
||||
* output stream is buffered or does not need additional buffering.</p>
|
||||
*
|
||||
* <p>It is more efficient than using a {@link java.io.OutputStreamWriter}
|
||||
* because it does not need to be wrapped in a
|
||||
* {@link java.io.BufferedWriter}. Creating multiple instances
|
||||
* of {@link java.io.BufferedWriter} has been shown to be very expensive.</p>
|
||||
*
|
||||
* <p>copy from: {@code com.sun.xml.internal.stream.writers.UTF8OutputStreamWriter}</p>
|
||||
*
|
||||
* @author Santiago.PericasGeertsen@sun.com
|
||||
*/
|
||||
public class UTF8OutputStreamWriter extends Writer {
|
||||
|
||||
/**
|
||||
* Undelying output stream. This class assumes that this
|
||||
* output stream does not need buffering.
|
||||
*/
|
||||
private final OutputStream out;
|
||||
|
||||
/**
|
||||
* Java represents chars that are not in the Basic Multilingual
|
||||
* Plane (BMP) in UTF-16. This int stores the first code unit
|
||||
* for a code point encoded in two UTF-16 code units.
|
||||
*/
|
||||
int lastUTF16CodePoint = 0;
|
||||
|
||||
/**
|
||||
* Creates a new UTF8OutputStreamWriter.
|
||||
*
|
||||
* @param out The underlying output stream.
|
||||
*/
|
||||
public UTF8OutputStreamWriter(final OutputStream out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final int c) throws IOException {
|
||||
// Check in we are encoding at high and low surrogates
|
||||
if (lastUTF16CodePoint != 0) {
|
||||
final int uc =
|
||||
(((lastUTF16CodePoint & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;
|
||||
|
||||
out.write(0xF0 | (uc >> 18));
|
||||
out.write(0x80 | ((uc >> 12) & 0x3F));
|
||||
out.write(0x80 | ((uc >> 6) & 0x3F));
|
||||
out.write(0x80 | (uc & 0x3F));
|
||||
|
||||
lastUTF16CodePoint = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, encode char as defined in UTF-8
|
||||
if (c < 0x80) {
|
||||
// 1 byte, 7 bits
|
||||
out.write((int) c);
|
||||
} else if (c < 0x800) {
|
||||
// 2 bytes, 11 bits
|
||||
out.write(0xC0 | (c >> 6)); // first 5
|
||||
out.write(0x80 | (c & 0x3F)); // second 6
|
||||
} else if (c <= '\uFFFF') {
|
||||
if (!isHighSurrogate(c) && !isLowSurrogate(c)) {
|
||||
// 3 bytes, 16 bits
|
||||
out.write(0xE0 | (c >> 12)); // first 4
|
||||
out.write(0x80 | ((c >> 6) & 0x3F)); // second 6
|
||||
out.write(0x80 | (c & 0x3F)); // third 6
|
||||
} else {
|
||||
lastUTF16CodePoint = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
||||
@Override
|
||||
public void write(final char[] cbuf) throws IOException {
|
||||
for (int i = 0; i < cbuf.length; i++) {
|
||||
write(cbuf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final char[] cbuf, final int off, final int len) throws IOException {
|
||||
for (int i = 0; i < len; i++) {
|
||||
write(cbuf[off + i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final String str) throws IOException {
|
||||
final int len = str.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
write(str.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final String str, final int off, final int len) throws IOException {
|
||||
for (int i = 0; i < len; i++) {
|
||||
write(str.charAt(off + i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (lastUTF16CodePoint != 0) {
|
||||
throw new IllegalStateException("Attempting to close a UTF8OutputStreamWriter"
|
||||
+ " while awaiting for a UTF-16 code unit");
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given character is a high surrogate
|
||||
*
|
||||
* @param c The character to check.
|
||||
* @return true if the character is a high surrogate.
|
||||
*/
|
||||
private static boolean isHighSurrogate(final int c) {
|
||||
return (0xD800 <= c && c <= 0xDBFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given character is a low surrogate
|
||||
*
|
||||
* @param c The character to check.
|
||||
* @return true if the character is a low surrogate.
|
||||
*/
|
||||
private static boolean isLowSurrogate(final int c) {
|
||||
return (0xDC00 <= c && c <= 0xDFFF);
|
||||
}
|
||||
}
|
||||
@@ -312,7 +312,7 @@ public class TypeUtil {
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static ParameterizedType toParameterizedType(Type type, final int interfaceIndex) {
|
||||
if(type instanceof TypeReference){
|
||||
if (type instanceof TypeReference) {
|
||||
type = ((TypeReference<?>) type).getType();
|
||||
}
|
||||
|
||||
@@ -473,6 +473,22 @@ public class TypeUtil {
|
||||
return parameterizedType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建泛型对象,例如:
|
||||
*
|
||||
* <pre>{@code
|
||||
* List<String> list = TypeUtil.createParameterizedType(List.class, String.class);
|
||||
* }</pre>
|
||||
*
|
||||
* @param rawType 原始类型,例如:List.class
|
||||
* @param actualTypeArguments 实际类型,例如:String.class
|
||||
* @return 泛型对象
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Type createParameterizedType(final Type rawType, final Type... actualTypeArguments) {
|
||||
return new ParameterizedTypeImpl(actualTypeArguments, null, rawType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得泛型变量对应的泛型实际类型,如果此变量没有对应的实际类型,返回null
|
||||
*
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
<bouncycastle.version>1.78.1</bouncycastle.version>
|
||||
<jjwt.version>0.12.6</jjwt.version>
|
||||
<jackson.version>2.17.2</jackson.version>
|
||||
<gson.version>2.11.0</gson.version>
|
||||
<fastjson2.version>2.0.41</fastjson2.version>
|
||||
<moshi.version>1.15.1</moshi.version>
|
||||
<jmh.version>1.37</jmh.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -69,13 +73,19 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<version>${gson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.41</version>
|
||||
<version>${fastjson2.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.moshi</groupId>
|
||||
<artifactId>moshi</artifactId>
|
||||
<version>1.15.1</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -98,6 +108,44 @@
|
||||
<version>${jjwt.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 基准性能测试 -->
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- 添加JMH测试资源 -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-jmh-resource</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>add-test-resource</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<resources>
|
||||
<directory>src/test/jmh</directory>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
package org.dromara.hutool.json.engine;
|
||||
|
||||
import org.dromara.hutool.core.io.stream.UTF8OutputStreamWriter;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.json.JSON;
|
||||
import org.dromara.hutool.json.JSONConfig;
|
||||
import org.dromara.hutool.json.JSONFactory;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
@@ -36,13 +37,21 @@ public class HutoolJSONEngine extends AbstractJSONEngine {
|
||||
private JSONFactory jsonFactory;
|
||||
|
||||
@Override
|
||||
public void serialize(final Object bean, final Writer writer) {
|
||||
public void serialize(final Object bean, final OutputStream out) {
|
||||
initEngine();
|
||||
final JSON json = jsonFactory.parse(bean);
|
||||
json.write(jsonFactory.ofWriter(writer,
|
||||
json.write(jsonFactory.ofWriter(new UTF8OutputStreamWriter(out),
|
||||
ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJsonString(final Object bean) {
|
||||
initEngine();
|
||||
final JSON json = jsonFactory.parse(bean);
|
||||
final boolean isPrettyPrint = ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false);
|
||||
return isPrettyPrint ? json.toStringPretty() : json.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(final Reader reader, final Object type) {
|
||||
initEngine();
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
|
||||
package org.dromara.hutool.json.engine;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import org.dromara.hutool.core.io.stream.FastByteArrayOutputStream;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* JSON引擎实现
|
||||
* JSON引擎接口,提供API:
|
||||
* <ul>
|
||||
* <li>serialize: JSON序列化, 将Bean对象序列化为JSON字符串</li>
|
||||
* <li>deserialize:JSON反序列化,将JSON字符串解析为Bean对象</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>{@link #init(JSONEngineConfig)}用于使用公共配置,初始化引擎。</p>
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
@@ -42,10 +48,10 @@ public interface JSONEngine {
|
||||
/**
|
||||
* 生成JSON数据(序列化),用于将指定的Bean对象通过Writer写出为JSON字符串
|
||||
*
|
||||
* @param bean Java Bean(POJO)对象
|
||||
* @param writer 写出到的Writer
|
||||
* @param bean Java Bean(POJO)对象
|
||||
* @param out 写出到的{@link OutputStream}
|
||||
*/
|
||||
void serialize(Object bean, Writer writer);
|
||||
void serialize(Object bean, OutputStream out);
|
||||
|
||||
/**
|
||||
* 解析JSON数据(反序列化),用于从Reader中读取JSON字符串,转换为Bean对象<br>
|
||||
@@ -65,9 +71,9 @@ public interface JSONEngine {
|
||||
* @return JSON字符串
|
||||
*/
|
||||
default String toJsonString(final Object bean) {
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
serialize(bean, stringWriter);
|
||||
return stringWriter.toString();
|
||||
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
|
||||
serialize(bean, out);
|
||||
return out.toString(CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,12 @@ import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.json.JSONException;
|
||||
|
||||
/**
|
||||
* JSON引擎工厂
|
||||
* JSON引擎工厂<br>
|
||||
* 通过SPI方式,动态查找用户引入的JSON实现库,并加载,提供两种加载方式:
|
||||
* <ul>
|
||||
* <li>{@link #getEngine()} 自动按照service文件中的顺序检查并加载第一个可用引擎。</li>
|
||||
* <li>{@link #createEngine(String)} 加载指定名称的引擎</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
@@ -48,10 +53,10 @@ public class JSONEngineFactory {
|
||||
*/
|
||||
public static JSONEngine createEngine(String engineName) throws JSONException {
|
||||
// fastjson名字兼容
|
||||
if(StrUtil.equalsIgnoreCase("fastjson", engineName)){
|
||||
if (StrUtil.equalsIgnoreCase("fastjson", engineName)) {
|
||||
engineName = "FastJSON2";
|
||||
}
|
||||
if(StrUtil.equalsIgnoreCase("hutool", engineName)){
|
||||
if (StrUtil.equalsIgnoreCase("hutool", engineName)) {
|
||||
engineName = "HutoolJSON";
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,16 @@ import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.alibaba.fastjson2.reader.ObjectReader;
|
||||
import com.alibaba.fastjson2.writer.ObjectWriter;
|
||||
import org.dromara.hutool.core.collection.ListUtil;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.json.engine.AbstractJSONEngine;
|
||||
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
@@ -53,19 +56,26 @@ public class FastJSON2Engine extends AbstractJSONEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final Object bean, final Writer writer) {
|
||||
initEngine();
|
||||
try (final JSONWriter jsonWriter = JSONWriter.of(this.writerContext)) {
|
||||
if (bean == null) {
|
||||
jsonWriter.writeNull();
|
||||
} else {
|
||||
jsonWriter.setRootObject(bean);
|
||||
final Class<?> valueClass = bean.getClass();
|
||||
final ObjectWriter<?> objectWriter = this.writerContext.getObjectWriter(valueClass, valueClass);
|
||||
objectWriter.write(jsonWriter, bean, null, null, 0);
|
||||
}
|
||||
public void serialize(final Object bean, final OutputStream out) {
|
||||
JSONWriter jsonWriter = null;
|
||||
try{
|
||||
jsonWriter = toJsonWriter(bean);
|
||||
jsonWriter.flushTo(out);
|
||||
} catch (final IOException e){
|
||||
throw new IORuntimeException(e);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(jsonWriter);
|
||||
}
|
||||
}
|
||||
|
||||
jsonWriter.flushTo(writer);
|
||||
@Override
|
||||
public String toJsonString(final Object bean) {
|
||||
JSONWriter jsonWriter = null;
|
||||
try{
|
||||
jsonWriter = toJsonWriter(bean);
|
||||
return jsonWriter.toString();
|
||||
} finally {
|
||||
IoUtil.closeQuietly(jsonWriter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,4 +126,26 @@ public class FastJSON2Engine extends AbstractJSONEngine {
|
||||
this.writerContext.setDateFormat(ObjUtil.defaultIfNull(config.getDateFormat(), "millis"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为JSONWriter
|
||||
*
|
||||
* @param bean 对象
|
||||
* @return JSONWriter
|
||||
*/
|
||||
private JSONWriter toJsonWriter(final Object bean) {
|
||||
initEngine();
|
||||
|
||||
final JSONWriter jsonWriter = JSONWriter.of(this.writerContext);
|
||||
if (bean == null) {
|
||||
jsonWriter.writeNull();
|
||||
} else {
|
||||
jsonWriter.setRootObject(bean);
|
||||
final Class<?> valueClass = bean.getClass();
|
||||
final ObjectWriter<?> objectWriter = this.writerContext.getObjectWriter(valueClass, valueClass);
|
||||
objectWriter.write(jsonWriter, bean, null, null, 0);
|
||||
}
|
||||
|
||||
return jsonWriter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ import java.util.Date;
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class DateSerDesc implements GsonTypeAdapter<Date> {
|
||||
public class DateGsonTypeAdapter implements GsonTypeAdapter<Date> {
|
||||
|
||||
/**
|
||||
* 默认日期格式化描述,默认为null,表示使用时间戳
|
||||
*/
|
||||
public static final DateSerDesc INSTANCE = new DateSerDesc(null);
|
||||
public static final DateGsonTypeAdapter INSTANCE = new DateGsonTypeAdapter(null);
|
||||
|
||||
private final String dateFormat;
|
||||
|
||||
@@ -44,7 +44,7 @@ public class DateSerDesc implements GsonTypeAdapter<Date> {
|
||||
*
|
||||
* @param dateFormat 日期格式
|
||||
*/
|
||||
public DateSerDesc(final String dateFormat) {
|
||||
public DateGsonTypeAdapter(final String dateFormat) {
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.dromara.hutool.json.engine.gson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.dromara.hutool.core.io.stream.UTF8OutputStreamWriter;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.lang.wrapper.Wrapper;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
@@ -25,8 +26,8 @@ import org.dromara.hutool.json.JSONException;
|
||||
import org.dromara.hutool.json.engine.AbstractJSONEngine;
|
||||
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -60,9 +61,15 @@ public class GsonEngine extends AbstractJSONEngine implements Wrapper<Gson> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final Object bean, final Writer writer) {
|
||||
public void serialize(final Object bean, final OutputStream out) {
|
||||
initEngine();
|
||||
gson.toJson(bean, writer);
|
||||
gson.toJson(bean, new UTF8OutputStreamWriter(out));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJsonString(final Object bean) {
|
||||
initEngine();
|
||||
return gson.toJson(bean);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -115,7 +122,7 @@ public class GsonEngine extends AbstractJSONEngine implements Wrapper<Gson> {
|
||||
*/
|
||||
private void registerDate(final GsonBuilder builder, final String dateFormat){
|
||||
// java date
|
||||
builder.registerTypeHierarchyAdapter(Date.class, new DateSerDesc(dateFormat));
|
||||
builder.registerTypeHierarchyAdapter(Date.class, new DateGsonTypeAdapter(dateFormat));
|
||||
builder.registerTypeHierarchyAdapter(TimeZone.class, TimeZoneGsonTypeAdapter.INSTANCE);
|
||||
|
||||
// java.time
|
||||
|
||||
@@ -35,8 +35,8 @@ import org.dromara.hutool.json.engine.AbstractJSONEngine;
|
||||
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Jackson引擎
|
||||
@@ -68,10 +68,20 @@ public class JacksonEngine extends AbstractJSONEngine implements Wrapper<ObjectM
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final Object bean, final Writer writer) {
|
||||
public void serialize(final Object bean, final OutputStream out) {
|
||||
initEngine();
|
||||
try {
|
||||
mapper.writeValue(writer, bean);
|
||||
mapper.writeValue(out, bean);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJsonString(final Object bean) {
|
||||
initEngine();
|
||||
try {
|
||||
return mapper.writeValueAsString(bean);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.json.engine.moshi;
|
||||
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.JsonReader;
|
||||
import com.squareup.moshi.JsonWriter;
|
||||
import org.dromara.hutool.core.date.DateUtil;
|
||||
import org.dromara.hutool.core.reflect.TypeUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 日期格式化适配器,用于moshi序列化与反序列化日期
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class DateMoshiAdapter extends JsonAdapter<Date> {
|
||||
|
||||
/**
|
||||
* 创建工厂
|
||||
*
|
||||
* @param dateFormat 日期格式
|
||||
* @return 创建工厂
|
||||
*/
|
||||
public static Factory createFactory(final String dateFormat){
|
||||
return (type, set, moshi) -> {
|
||||
if(Date.class.isAssignableFrom(TypeUtil.getClass(type))){
|
||||
return new DateMoshiAdapter(dateFormat);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private final String dateFormat;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param dateFormat 日期格式
|
||||
*/
|
||||
public DateMoshiAdapter(final String dateFormat) {
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(final JsonWriter jsonWriter, final Date date) throws IOException {
|
||||
if(null == date){
|
||||
jsonWriter.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
if(StrUtil.isEmpty(dateFormat)){
|
||||
jsonWriter.value(date.getTime());
|
||||
}else{
|
||||
jsonWriter.value(DateUtil.format(date, dateFormat));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date fromJson(final JsonReader jsonReader) throws IOException {
|
||||
if(jsonReader.peek() == JsonReader.Token.NULL){
|
||||
return jsonReader.nextNull();
|
||||
}
|
||||
|
||||
return StrUtil.isEmpty(dateFormat) ?
|
||||
DateUtil.date(jsonReader.nextLong()) :
|
||||
DateUtil.parse(jsonReader.nextString(), dateFormat);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.json.engine.moshi;
|
||||
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.Moshi;
|
||||
import okio.BufferedSink;
|
||||
import okio.BufferedSource;
|
||||
import okio.Okio;
|
||||
import org.dromara.hutool.core.io.stream.ReaderInputStream;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.lang.wrapper.Wrapper;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.json.JSONException;
|
||||
import org.dromara.hutool.json.engine.AbstractJSONEngine;
|
||||
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* Moshi引擎实现
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class MoshiEngine extends AbstractJSONEngine implements Wrapper<Moshi> {
|
||||
|
||||
private Moshi moshi;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public MoshiEngine() {
|
||||
// issue#IABWBL JDK8下,在IDEA旗舰版加载Spring boot插件时,启动应用不会检查字段类是否存在
|
||||
// 此处构造时调用下这个类,以便触发类是否存在的检查
|
||||
Assert.notNull(Moshi.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Moshi getRaw() {
|
||||
initEngine();
|
||||
return this.moshi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final Object bean, final OutputStream out) {
|
||||
final BufferedSink sink = Okio.buffer(Okio.sink(out));
|
||||
try {
|
||||
getAdapter(this.moshi, bean.getClass()).toJson(sink, bean);
|
||||
} catch (final IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJsonString(final Object bean) {
|
||||
final JsonAdapter<Object> adapter = getAdapter(this.moshi, bean.getClass());
|
||||
return adapter.toJson(bean);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T deserialize(final Reader reader, final Object type) {
|
||||
initEngine();
|
||||
final BufferedSource source = Okio.buffer(Okio.source(new ReaderInputStream(reader, CharsetUtil.UTF_8)));
|
||||
final JsonAdapter<Object> adapter = this.moshi.adapter((Type) type);
|
||||
try {
|
||||
return (T) adapter.fromJson(source);
|
||||
} catch (final IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() {
|
||||
this.moshi = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initEngine() {
|
||||
if (null != this.moshi) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 自定义配置
|
||||
final JSONEngineConfig config = ObjUtil.defaultIfNull(this.config, JSONEngineConfig::of);
|
||||
final Moshi.Builder builder = new Moshi.Builder();
|
||||
|
||||
// 注册日期相关序列化描述
|
||||
final String dateFormat = config.getDateFormat();
|
||||
registerDate(builder, dateFormat);
|
||||
|
||||
this.moshi = builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并配置{@link JsonAdapter}
|
||||
*
|
||||
* @param moshi {@link Moshi}
|
||||
* @param type Bean类型
|
||||
* @return this
|
||||
*/
|
||||
private JsonAdapter<Object> getAdapter(final Moshi moshi, final Type type) {
|
||||
initEngine();
|
||||
JsonAdapter<Object> adapter = this.moshi.adapter(type);
|
||||
if (ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false)) {
|
||||
adapter = adapter.indent(" ");
|
||||
}
|
||||
if (!ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isIgnoreNullValue, true)) {
|
||||
adapter = adapter.serializeNulls();
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册日期相关序列化描述
|
||||
*
|
||||
* @param builder Gson构造器
|
||||
* @param dateFormat 日期格式
|
||||
*/
|
||||
private void registerDate(final Moshi.Builder builder, final String dateFormat) {
|
||||
builder.add(DateMoshiAdapter.createFactory(dateFormat));
|
||||
builder.add(LocalDateTime.class, new TemporalMoshiAdapter(LocalDateTime.class, dateFormat));
|
||||
builder.add(LocalDate.class, new TemporalMoshiAdapter(LocalDate.class, dateFormat));
|
||||
builder.add(LocalTime.class, new TemporalMoshiAdapter(LocalTime.class, dateFormat));
|
||||
builder.add(TimeZoneMoshiAdapter.FACTORY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.json.engine.moshi;
|
||||
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.JsonReader;
|
||||
import com.squareup.moshi.JsonWriter;
|
||||
import org.dromara.hutool.core.convert.ConvertUtil;
|
||||
import org.dromara.hutool.core.date.TimeUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
|
||||
/**
|
||||
* 时间类型适配器,用于处理时间类型的序列化与反序列化
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class TemporalMoshiAdapter extends JsonAdapter<TemporalAccessor> {
|
||||
|
||||
private final Class<? extends TemporalAccessor> type;
|
||||
private final String dateFormat;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param type 时间类型
|
||||
* @param dateFormat 日期格式
|
||||
*/
|
||||
public TemporalMoshiAdapter(final Class<? extends TemporalAccessor> type, final String dateFormat) {
|
||||
this.type = type;
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(final JsonWriter jsonWriter, final TemporalAccessor src) throws IOException {
|
||||
if (null == src){
|
||||
jsonWriter.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (StrUtil.isEmpty(dateFormat)) {
|
||||
jsonWriter.value(TimeUtil.toEpochMilli(src));
|
||||
} else {
|
||||
jsonWriter.value(TimeUtil.format(src, dateFormat));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemporalAccessor fromJson(final JsonReader jsonReader) throws IOException {
|
||||
if(jsonReader.peek() == JsonReader.Token.NULL){
|
||||
return jsonReader.nextNull();
|
||||
}
|
||||
return StrUtil.isEmpty(dateFormat) ?
|
||||
ConvertUtil.convert(this.type, jsonReader.nextLong()) :
|
||||
ConvertUtil.convert(this.type, TimeUtil.parse(jsonReader.nextString(), dateFormat));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.json.engine.moshi;
|
||||
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.JsonReader;
|
||||
import com.squareup.moshi.JsonWriter;
|
||||
import com.squareup.moshi.Moshi;
|
||||
import org.dromara.hutool.core.reflect.TypeUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* TimeZone类型适配器
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class TimeZoneMoshiAdapter extends JsonAdapter<TimeZone> {
|
||||
|
||||
/**
|
||||
* 创建工厂
|
||||
*/
|
||||
public static final Factory FACTORY = new Factory() {
|
||||
@Override
|
||||
public JsonAdapter<?> create(final Type type, final Set<? extends Annotation> set, final Moshi moshi) {
|
||||
if(TimeZone.class.isAssignableFrom(TypeUtil.getClass(type))){
|
||||
return INSTANCE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 单例
|
||||
*/
|
||||
public static final TimeZoneMoshiAdapter INSTANCE = new TimeZoneMoshiAdapter();
|
||||
|
||||
@Override
|
||||
public void toJson(final JsonWriter jsonWriter, final TimeZone timeZone) throws IOException {
|
||||
if(null == timeZone){
|
||||
jsonWriter.nullValue();
|
||||
return;
|
||||
}
|
||||
jsonWriter.value(timeZone.getID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZone fromJson(final JsonReader jsonReader) throws IOException {
|
||||
return TimeZone.getTimeZone(jsonReader.nextString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Moshi引擎实现<br>
|
||||
* https://github.com/square/moshi
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
package org.dromara.hutool.json.engine.moshi;
|
||||
@@ -65,7 +65,7 @@ public class JSONXMLSerializer {
|
||||
* @return A string.
|
||||
* @throws JSONException JSON解析异常
|
||||
*/
|
||||
public static String toXml(JSON json, final String tagName, final String... contentKeys) throws JSONException {
|
||||
public static String toXml(final JSON json, final String tagName, final String... contentKeys) throws JSONException {
|
||||
if (null == json) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -17,4 +17,5 @@
|
||||
org.dromara.hutool.json.engine.jackson.JacksonEngine
|
||||
org.dromara.hutool.json.engine.gson.GsonEngine
|
||||
org.dromara.hutool.json.engine.fastjson.FastJSON2Engine
|
||||
org.dromara.hutool.json.engine.moshi.MoshiEngine
|
||||
org.dromara.hutool.json.engine.HutoolJSONEngine
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.json.engine;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class BeanWithLocalDateTime {
|
||||
private LocalDateTime date;
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import org.dromara.hutool.json.engine.jackson.JacksonEngine;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@@ -72,12 +71,10 @@ public class JSONEngineFactoryTest {
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine("gson");
|
||||
assertEquals(GsonEngine.class, engine.getClass());
|
||||
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
final TestBean testBean = new TestBean("张三", 18, true);
|
||||
engine.serialize(testBean, stringWriter);
|
||||
|
||||
final String jsonStr = "{\"name\":\"张三\",\"age\":18,\"gender\":true}";
|
||||
assertEquals(jsonStr, stringWriter.toString());
|
||||
assertEquals(jsonStr, engine.toJsonString(testBean));
|
||||
|
||||
final TestBean testBean1 = engine.deserialize(new StringReader(jsonStr), TestBean.class);
|
||||
assertEquals(testBean, testBean1);
|
||||
@@ -88,12 +85,10 @@ public class JSONEngineFactoryTest {
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine("fastjson");
|
||||
assertEquals(FastJSON2Engine.class, engine.getClass());
|
||||
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
final TestBean testBean = new TestBean("张三", 18, true);
|
||||
engine.serialize(testBean, stringWriter);
|
||||
|
||||
final String jsonStr = "{\"name\":\"张三\",\"age\":18,\"gender\":true}";
|
||||
assertEquals(jsonStr, stringWriter.toString());
|
||||
assertEquals(jsonStr, engine.toJsonString(testBean));
|
||||
|
||||
final TestBean testBean1 = engine.deserialize(new StringReader(jsonStr), TestBean.class);
|
||||
assertEquals(testBean, testBean1);
|
||||
@@ -104,12 +99,10 @@ public class JSONEngineFactoryTest {
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine("hutoolJSON");
|
||||
assertEquals(HutoolJSONEngine.class, engine.getClass());
|
||||
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
final TestBean testBean = new TestBean("张三", 18, true);
|
||||
engine.serialize(testBean, stringWriter);
|
||||
|
||||
final String jsonStr = "{\"name\":\"张三\",\"age\":18,\"gender\":true}";
|
||||
assertEquals(jsonStr, stringWriter.toString());
|
||||
assertEquals(jsonStr, engine.toJsonString(testBean));
|
||||
|
||||
final TestBean testBean1 = engine.deserialize(new StringReader(jsonStr), TestBean.class);
|
||||
assertEquals(testBean, testBean1);
|
||||
|
||||
@@ -20,15 +20,16 @@ import lombok.Data;
|
||||
import org.dromara.hutool.core.date.DateTime;
|
||||
import org.dromara.hutool.core.date.DateUtil;
|
||||
import org.dromara.hutool.core.date.TimeUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class JSONEngineTest {
|
||||
|
||||
private final String[] engineNames = {"jackson", "gson", "fastjson", "hutool"};
|
||||
private final String[] engineNames = {"jackson", "gson", "fastjson", "moshi", "hutool"};
|
||||
|
||||
@Test
|
||||
void writeDateFormatTest() {
|
||||
@@ -61,10 +62,10 @@ public class JSONEngineTest {
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine(engineName);
|
||||
|
||||
final String jsonString = engine.toJsonString(bean);
|
||||
Assertions.assertEquals("{\"date1\":1704042741000,\"date2\":1704042741000}", jsonString);
|
||||
assertEquals("{\"date1\":1704042741000,\"date2\":1704042741000}", jsonString);
|
||||
|
||||
engine.init(JSONEngineConfig.of().setDateFormat("yyyy-MM-dd HH:mm:ss"));
|
||||
Assertions.assertEquals("{\"date1\":\"2024-01-01 01:12:21\",\"date2\":\"2024-01-01 01:12:21\"}", engine.toJsonString(bean));
|
||||
assertEquals("{\"date1\":\"2024-01-01 01:12:21\",\"date2\":\"2024-01-01 01:12:21\"}", engine.toJsonString(bean));
|
||||
}
|
||||
|
||||
void assertWriteLocalDateFormat(final String engineName) {
|
||||
@@ -73,10 +74,10 @@ public class JSONEngineTest {
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine(engineName);
|
||||
|
||||
final String jsonString = engine.toJsonString(bean);
|
||||
Assertions.assertEquals("{\"date\":1704038400000}", jsonString);
|
||||
assertEquals("{\"date\":1704038400000}", jsonString);
|
||||
|
||||
engine.init(JSONEngineConfig.of().setDateFormat("yyyy-MM-dd HH:mm:ss"));
|
||||
Assertions.assertEquals("{\"date\":\"2024-01-01 00:00:00\"}", engine.toJsonString(bean));
|
||||
assertEquals("{\"date\":\"2024-01-01 00:00:00\"}", engine.toJsonString(bean));
|
||||
}
|
||||
|
||||
private void assertWriteNull(final String engineName) {
|
||||
@@ -84,11 +85,11 @@ public class JSONEngineTest {
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine(engineName);
|
||||
|
||||
String jsonString = engine.toJsonString(bean);
|
||||
Assertions.assertEquals("{}", jsonString);
|
||||
assertEquals("{}", jsonString);
|
||||
|
||||
engine.init(JSONEngineConfig.of().setIgnoreNullValue(false));
|
||||
jsonString = engine.toJsonString(bean);
|
||||
Assertions.assertEquals("{\"date1\":null,\"date2\":null}", jsonString);
|
||||
assertEquals("{\"date1\":null,\"date2\":null}", jsonString);
|
||||
}
|
||||
|
||||
private void assertWriteTimeZone(final String engineName) {
|
||||
@@ -96,17 +97,17 @@ public class JSONEngineTest {
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine(engineName);
|
||||
|
||||
String jsonString = engine.toJsonString(timeZone);
|
||||
Assertions.assertEquals("\"GMT+08:00\"", jsonString);
|
||||
assertEquals("\"GMT+08:00\"", jsonString);
|
||||
|
||||
engine.init(JSONEngineConfig.of().setIgnoreNullValue(false));
|
||||
jsonString = engine.toJsonString(timeZone);
|
||||
Assertions.assertEquals("\"GMT+08:00\"", jsonString);
|
||||
assertEquals("\"GMT+08:00\"", jsonString);
|
||||
}
|
||||
|
||||
private void assertEmptyBeanToJson(final String engineName){
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine(engineName);
|
||||
final String jsonString = engine.toJsonString(new EmptyBean());
|
||||
Assertions.assertEquals("{}", jsonString);
|
||||
assertEquals("{}", jsonString);
|
||||
}
|
||||
|
||||
@Data
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.json.engine;
|
||||
|
||||
import org.dromara.hutool.core.date.DateTime;
|
||||
import org.dromara.hutool.core.date.DateUtil;
|
||||
import org.dromara.hutool.core.date.TimeUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class MoshiTest {
|
||||
|
||||
@Test
|
||||
void writeLocalDateFormatTest() {
|
||||
final DateTime date = DateUtil.parse("2024-01-01 01:12:21");
|
||||
final BeanWithLocalDateTime bean = new BeanWithLocalDateTime(TimeUtil.of(date));
|
||||
final JSONEngine engine = JSONEngineFactory.createEngine("moshi");
|
||||
|
||||
final String jsonString = engine.toJsonString(bean);
|
||||
Assertions.assertEquals("{\"date\":1704042741000}", jsonString);
|
||||
|
||||
engine.init(JSONEngineConfig.of().setDateFormat("yyyy-MM-dd HH:mm:ss"));
|
||||
Assertions.assertEquals("{\"date\":\"2024-01-01 01:12:21\"}", engine.toJsonString(bean));
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.json;
|
||||
package org.dromara.hutool.json.serializer;
|
||||
|
||||
import lombok.ToString;
|
||||
import org.dromara.hutool.json.serializer.JSONDeserializer;
|
||||
import org.dromara.hutool.json.serializer.JSONSerializer;
|
||||
import org.dromara.hutool.json.serializer.TypeAdapterManager;
|
||||
import org.dromara.hutool.json.JSONObject;
|
||||
import org.dromara.hutool.json.JSONUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.json.engine;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)//每次执行平均花费时间
|
||||
@Warmup(iterations = 5, time = 1) //预热5次调用
|
||||
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 执行5此,每次1秒
|
||||
@Threads(1) //单线程
|
||||
@Fork(1) //
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS) // 单位:纳秒
|
||||
@State(Scope.Benchmark) // 共享域
|
||||
public class ToJsonStrJmh {
|
||||
|
||||
private JSONEngine jacksonEngine;
|
||||
private JSONEngine gsonEngine;
|
||||
private JSONEngine fastJSONEngine;
|
||||
private JSONEngine moshiEngine;
|
||||
private JSONEngine hutoolEngine;
|
||||
|
||||
private TestBean testBean;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
jacksonEngine = JSONEngineFactory.createEngine("jackson");
|
||||
gsonEngine = JSONEngineFactory.createEngine("gson");
|
||||
fastJSONEngine = JSONEngineFactory.createEngine("fastjson");
|
||||
moshiEngine = JSONEngineFactory.createEngine("moshi");
|
||||
hutoolEngine = JSONEngineFactory.createEngine("hutool");
|
||||
|
||||
testBean = new TestBean("张三", 18, true, new Date(), null);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void jacksonJmh() {
|
||||
jacksonEngine.toJsonString(testBean);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void gsonJmh() {
|
||||
gsonEngine.toJsonString(testBean);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void fastJSONJmh() {
|
||||
fastJSONEngine.toJsonString(testBean);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void moshiJSONJmh() {
|
||||
moshiEngine.toJsonString(testBean);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void hutoolJSONJmh() {
|
||||
hutoolEngine.toJsonString(testBean);
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
static class TestBean {
|
||||
private String name;
|
||||
private int age;
|
||||
private boolean gender;
|
||||
private Date createDate;
|
||||
private Object nullObj;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user