mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
fix code and add UTF8OutputStreamWriter
This commit is contained in:
parent
4db6ac1f34
commit
924357730e
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -140,7 +140,6 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<resources>
|
<resources>
|
||||||
<directory>src/test/jmh</directory>
|
<directory>src/test/jmh</directory>
|
||||||
<targetPath>my-resources</targetPath>
|
|
||||||
</resources>
|
</resources>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
@ -16,13 +16,14 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.json.engine;
|
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.core.util.ObjUtil;
|
||||||
import org.dromara.hutool.json.JSON;
|
import org.dromara.hutool.json.JSON;
|
||||||
import org.dromara.hutool.json.JSONConfig;
|
import org.dromara.hutool.json.JSONConfig;
|
||||||
import org.dromara.hutool.json.JSONFactory;
|
import org.dromara.hutool.json.JSONFactory;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,13 +37,21 @@ public class HutoolJSONEngine extends AbstractJSONEngine {
|
|||||||
private JSONFactory jsonFactory;
|
private JSONFactory jsonFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final Object bean, final Writer writer) {
|
public void serialize(final Object bean, final OutputStream out) {
|
||||||
initEngine();
|
initEngine();
|
||||||
final JSON json = jsonFactory.parse(bean);
|
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)));
|
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
|
@Override
|
||||||
public <T> T deserialize(final Reader reader, final Object type) {
|
public <T> T deserialize(final Reader reader, final Object type) {
|
||||||
initEngine();
|
initEngine();
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.json.engine;
|
package org.dromara.hutool.json.engine;
|
||||||
|
|
||||||
import java.io.Reader;
|
import org.dromara.hutool.core.io.stream.FastByteArrayOutputStream;
|
||||||
import java.io.StringReader;
|
import org.dromara.hutool.core.util.CharsetUtil;
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.io.Writer;
|
import java.io.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON引擎实现
|
* JSON引擎实现
|
||||||
@ -43,9 +43,9 @@ public interface JSONEngine {
|
|||||||
* 生成JSON数据(序列化),用于将指定的Bean对象通过Writer写出为JSON字符串
|
* 生成JSON数据(序列化),用于将指定的Bean对象通过Writer写出为JSON字符串
|
||||||
*
|
*
|
||||||
* @param bean Java Bean(POJO)对象
|
* @param bean Java Bean(POJO)对象
|
||||||
* @param writer 写出到的Writer
|
* @param out 写出到的{@link OutputStream}
|
||||||
*/
|
*/
|
||||||
void serialize(Object bean, Writer writer);
|
void serialize(Object bean, OutputStream out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析JSON数据(反序列化),用于从Reader中读取JSON字符串,转换为Bean对象<br>
|
* 解析JSON数据(反序列化),用于从Reader中读取JSON字符串,转换为Bean对象<br>
|
||||||
@ -65,9 +65,9 @@ public interface JSONEngine {
|
|||||||
* @return JSON字符串
|
* @return JSON字符串
|
||||||
*/
|
*/
|
||||||
default String toJsonString(final Object bean) {
|
default String toJsonString(final Object bean) {
|
||||||
final StringWriter stringWriter = new StringWriter();
|
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
|
||||||
serialize(bean, stringWriter);
|
serialize(bean, out);
|
||||||
return stringWriter.toString();
|
return out.toString(CharsetUtil.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,13 +22,16 @@ import com.alibaba.fastjson2.JSONWriter;
|
|||||||
import com.alibaba.fastjson2.reader.ObjectReader;
|
import com.alibaba.fastjson2.reader.ObjectReader;
|
||||||
import com.alibaba.fastjson2.writer.ObjectWriter;
|
import com.alibaba.fastjson2.writer.ObjectWriter;
|
||||||
import org.dromara.hutool.core.collection.ListUtil;
|
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.lang.Assert;
|
||||||
import org.dromara.hutool.core.util.ObjUtil;
|
import org.dromara.hutool.core.util.ObjUtil;
|
||||||
import org.dromara.hutool.json.engine.AbstractJSONEngine;
|
import org.dromara.hutool.json.engine.AbstractJSONEngine;
|
||||||
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -53,19 +56,26 @@ public class FastJSON2Engine extends AbstractJSONEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final Object bean, final Writer writer) {
|
public void serialize(final Object bean, final OutputStream out) {
|
||||||
initEngine();
|
JSONWriter jsonWriter = null;
|
||||||
try (final JSONWriter jsonWriter = JSONWriter.of(this.writerContext)) {
|
try{
|
||||||
if (bean == null) {
|
jsonWriter = toJsonWriter(bean);
|
||||||
jsonWriter.writeNull();
|
jsonWriter.flushTo(out);
|
||||||
} else {
|
} catch (final IOException e){
|
||||||
jsonWriter.setRootObject(bean);
|
throw new IORuntimeException(e);
|
||||||
final Class<?> valueClass = bean.getClass();
|
} finally {
|
||||||
final ObjectWriter<?> objectWriter = this.writerContext.getObjectWriter(valueClass, valueClass);
|
IoUtil.closeQuietly(jsonWriter);
|
||||||
objectWriter.write(jsonWriter, bean, null, null, 0);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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"));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package org.dromara.hutool.json.engine.gson;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
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.Assert;
|
||||||
import org.dromara.hutool.core.lang.wrapper.Wrapper;
|
import org.dromara.hutool.core.lang.wrapper.Wrapper;
|
||||||
import org.dromara.hutool.core.util.ObjUtil;
|
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.AbstractJSONEngine;
|
||||||
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -60,9 +61,15 @@ public class GsonEngine extends AbstractJSONEngine implements Wrapper<Gson> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final Object bean, final Writer writer) {
|
public void serialize(final Object bean, final OutputStream out) {
|
||||||
initEngine();
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -35,8 +35,8 @@ import org.dromara.hutool.json.engine.AbstractJSONEngine;
|
|||||||
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jackson引擎
|
* Jackson引擎
|
||||||
@ -68,10 +68,20 @@ public class JacksonEngine extends AbstractJSONEngine implements Wrapper<ObjectM
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final Object bean, final Writer writer) {
|
public void serialize(final Object bean, final OutputStream out) {
|
||||||
initEngine();
|
initEngine();
|
||||||
try {
|
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) {
|
} catch (final IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -73,11 +73,14 @@ public class DateMoshiAdapter extends JsonAdapter<Date> {
|
|||||||
}else{
|
}else{
|
||||||
jsonWriter.value(DateUtil.format(date, dateFormat));
|
jsonWriter.value(DateUtil.format(date, dateFormat));
|
||||||
}
|
}
|
||||||
jsonWriter.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date fromJson(final JsonReader jsonReader) throws IOException {
|
public Date fromJson(final JsonReader jsonReader) throws IOException {
|
||||||
|
if(jsonReader.peek() == JsonReader.Token.NULL){
|
||||||
|
return jsonReader.nextNull();
|
||||||
|
}
|
||||||
|
|
||||||
return StrUtil.isEmpty(dateFormat) ?
|
return StrUtil.isEmpty(dateFormat) ?
|
||||||
DateUtil.date(jsonReader.nextLong()) :
|
DateUtil.date(jsonReader.nextLong()) :
|
||||||
DateUtil.parse(jsonReader.nextString(), dateFormat);
|
DateUtil.parse(jsonReader.nextString(), dateFormat);
|
||||||
|
@ -22,7 +22,6 @@ import okio.BufferedSink;
|
|||||||
import okio.BufferedSource;
|
import okio.BufferedSource;
|
||||||
import okio.Okio;
|
import okio.Okio;
|
||||||
import org.dromara.hutool.core.io.stream.ReaderInputStream;
|
import org.dromara.hutool.core.io.stream.ReaderInputStream;
|
||||||
import org.dromara.hutool.core.io.stream.WriterOutputStream;
|
|
||||||
import org.dromara.hutool.core.lang.Assert;
|
import org.dromara.hutool.core.lang.Assert;
|
||||||
import org.dromara.hutool.core.lang.wrapper.Wrapper;
|
import org.dromara.hutool.core.lang.wrapper.Wrapper;
|
||||||
import org.dromara.hutool.core.util.CharsetUtil;
|
import org.dromara.hutool.core.util.CharsetUtil;
|
||||||
@ -32,8 +31,8 @@ import org.dromara.hutool.json.engine.AbstractJSONEngine;
|
|||||||
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
import org.dromara.hutool.json.engine.JSONEngineConfig;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -64,27 +63,22 @@ public class MoshiEngine extends AbstractJSONEngine implements Wrapper<Moshi> {
|
|||||||
return this.moshi;
|
return this.moshi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final Object bean, final Writer writer) {
|
public void serialize(final Object bean, final OutputStream out) {
|
||||||
initEngine();
|
final BufferedSink sink = Okio.buffer(Okio.sink(out));
|
||||||
final BufferedSink sink = Okio.buffer(Okio.sink(new WriterOutputStream(writer, CharsetUtil.UTF_8)));
|
|
||||||
JsonAdapter<Object> adapter = (JsonAdapter<Object>) this.moshi.adapter(bean.getClass());
|
|
||||||
|
|
||||||
if(ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false)){
|
|
||||||
adapter = adapter.indent(" ");
|
|
||||||
}
|
|
||||||
if(!ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isIgnoreNullValue, true)){
|
|
||||||
adapter = adapter.serializeNulls();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
adapter.toJson(sink, bean);
|
getAdapter(this.moshi, bean.getClass()).toJson(sink, bean);
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new JSONException(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")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public <T> T deserialize(final Reader reader, final Object type) {
|
public <T> T deserialize(final Reader reader, final Object type) {
|
||||||
@ -120,13 +114,32 @@ public class MoshiEngine extends AbstractJSONEngine implements Wrapper<Moshi> {
|
|||||||
this.moshi = builder.build();
|
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 builder Gson构造器
|
||||||
* @param dateFormat 日期格式
|
* @param dateFormat 日期格式
|
||||||
*/
|
*/
|
||||||
private void registerDate(final Moshi.Builder builder, final String dateFormat){
|
private void registerDate(final Moshi.Builder builder, final String dateFormat) {
|
||||||
builder.add(DateMoshiAdapter.createFactory(dateFormat));
|
builder.add(DateMoshiAdapter.createFactory(dateFormat));
|
||||||
builder.add(LocalDateTime.class, new TemporalMoshiAdapter(LocalDateTime.class, dateFormat));
|
builder.add(LocalDateTime.class, new TemporalMoshiAdapter(LocalDateTime.class, dateFormat));
|
||||||
builder.add(LocalDate.class, new TemporalMoshiAdapter(LocalDate.class, dateFormat));
|
builder.add(LocalDate.class, new TemporalMoshiAdapter(LocalDate.class, dateFormat));
|
||||||
|
@ -60,11 +60,13 @@ public class TemporalMoshiAdapter extends JsonAdapter<TemporalAccessor> {
|
|||||||
} else {
|
} else {
|
||||||
jsonWriter.value(TimeUtil.format(src, dateFormat));
|
jsonWriter.value(TimeUtil.format(src, dateFormat));
|
||||||
}
|
}
|
||||||
jsonWriter.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TemporalAccessor fromJson(final JsonReader jsonReader) throws IOException {
|
public TemporalAccessor fromJson(final JsonReader jsonReader) throws IOException {
|
||||||
|
if(jsonReader.peek() == JsonReader.Token.NULL){
|
||||||
|
return jsonReader.nextNull();
|
||||||
|
}
|
||||||
return StrUtil.isEmpty(dateFormat) ?
|
return StrUtil.isEmpty(dateFormat) ?
|
||||||
ConvertUtil.convert(this.type, jsonReader.nextLong()) :
|
ConvertUtil.convert(this.type, jsonReader.nextLong()) :
|
||||||
ConvertUtil.convert(this.type, TimeUtil.parse(jsonReader.nextString(), dateFormat));
|
ConvertUtil.convert(this.type, TimeUtil.parse(jsonReader.nextString(), dateFormat));
|
||||||
|
@ -60,7 +60,6 @@ public class TimeZoneMoshiAdapter extends JsonAdapter<TimeZone> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
jsonWriter.value(timeZone.getID());
|
jsonWriter.value(timeZone.getID());
|
||||||
jsonWriter.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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 org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
@ -72,12 +71,10 @@ public class JSONEngineFactoryTest {
|
|||||||
final JSONEngine engine = JSONEngineFactory.createEngine("gson");
|
final JSONEngine engine = JSONEngineFactory.createEngine("gson");
|
||||||
assertEquals(GsonEngine.class, engine.getClass());
|
assertEquals(GsonEngine.class, engine.getClass());
|
||||||
|
|
||||||
final StringWriter stringWriter = new StringWriter();
|
|
||||||
final TestBean testBean = new TestBean("张三", 18, true);
|
final TestBean testBean = new TestBean("张三", 18, true);
|
||||||
engine.serialize(testBean, stringWriter);
|
|
||||||
|
|
||||||
final String jsonStr = "{\"name\":\"张三\",\"age\":18,\"gender\":true}";
|
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);
|
final TestBean testBean1 = engine.deserialize(new StringReader(jsonStr), TestBean.class);
|
||||||
assertEquals(testBean, testBean1);
|
assertEquals(testBean, testBean1);
|
||||||
@ -88,12 +85,10 @@ public class JSONEngineFactoryTest {
|
|||||||
final JSONEngine engine = JSONEngineFactory.createEngine("fastjson");
|
final JSONEngine engine = JSONEngineFactory.createEngine("fastjson");
|
||||||
assertEquals(FastJSON2Engine.class, engine.getClass());
|
assertEquals(FastJSON2Engine.class, engine.getClass());
|
||||||
|
|
||||||
final StringWriter stringWriter = new StringWriter();
|
|
||||||
final TestBean testBean = new TestBean("张三", 18, true);
|
final TestBean testBean = new TestBean("张三", 18, true);
|
||||||
engine.serialize(testBean, stringWriter);
|
|
||||||
|
|
||||||
final String jsonStr = "{\"name\":\"张三\",\"age\":18,\"gender\":true}";
|
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);
|
final TestBean testBean1 = engine.deserialize(new StringReader(jsonStr), TestBean.class);
|
||||||
assertEquals(testBean, testBean1);
|
assertEquals(testBean, testBean1);
|
||||||
@ -104,12 +99,10 @@ public class JSONEngineFactoryTest {
|
|||||||
final JSONEngine engine = JSONEngineFactory.createEngine("hutoolJSON");
|
final JSONEngine engine = JSONEngineFactory.createEngine("hutoolJSON");
|
||||||
assertEquals(HutoolJSONEngine.class, engine.getClass());
|
assertEquals(HutoolJSONEngine.class, engine.getClass());
|
||||||
|
|
||||||
final StringWriter stringWriter = new StringWriter();
|
|
||||||
final TestBean testBean = new TestBean("张三", 18, true);
|
final TestBean testBean = new TestBean("张三", 18, true);
|
||||||
engine.serialize(testBean, stringWriter);
|
|
||||||
|
|
||||||
final String jsonStr = "{\"name\":\"张三\",\"age\":18,\"gender\":true}";
|
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);
|
final TestBean testBean1 = engine.deserialize(new StringReader(jsonStr), TestBean.class);
|
||||||
assertEquals(testBean, testBean1);
|
assertEquals(testBean, testBean1);
|
||||||
|
@ -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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.dromara.hutool.json;
|
package org.dromara.hutool.json.serializer;
|
||||||
|
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import org.dromara.hutool.json.serializer.JSONDeserializer;
|
import org.dromara.hutool.json.JSONObject;
|
||||||
import org.dromara.hutool.json.serializer.JSONSerializer;
|
import org.dromara.hutool.json.JSONUtil;
|
||||||
import org.dromara.hutool.json.serializer.TypeAdapterManager;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
Loading…
x
Reference in New Issue
Block a user