add methods

This commit is contained in:
Looly 2024-10-01 22:14:02 +08:00
parent 64a04fdfd1
commit 463d8d9c6f
10 changed files with 191 additions and 71 deletions

View File

@ -117,7 +117,7 @@ public final class InternalJSONUtil {
.setIgnoreError(config.isIgnoreError()) .setIgnoreError(config.isIgnoreError())
.setIgnoreNullValue(config.isIgnoreNullValue()) .setIgnoreNullValue(config.isIgnoreNullValue())
.setTransientSupport(config.isTransientSupport()) .setTransientSupport(config.isTransientSupport())
.setConverter((targetType, value) -> mapper.toJSON(value)); .setConverter((targetType, value) -> mapper.toJSON(value, false));
} }
/** /**

View File

@ -120,7 +120,8 @@ public class JSONArray extends ListWrapper<JSON> implements JSON, JSONGetter<Int
* @return this. * @return this.
*/ */
public JSONArray addObj(final Object value) { public JSONArray addObj(final Object value) {
this.add(this.factory.getMapper().toJSON(value)); // add时如果value为字符串不解析而是作为JSONPrimitive对待
this.add(this.factory.getMapper().toJSON(value, false));
return this; return this;
} }
@ -171,7 +172,8 @@ public class JSONArray extends ListWrapper<JSON> implements JSON, JSONGetter<Int
* @return this * @return this
*/ */
public JSONArray setObj(final int index, final Object element) { public JSONArray setObj(final int index, final Object element) {
set(index, this.factory.getMapper().toJSON(element)); // set时如果value为字符串不解析而是作为JSONPrimitive对待
set(index, this.factory.getMapper().toJSON(element, false));
return this; return this;
} }

View File

@ -267,7 +267,12 @@ public class JSONFactory {
// region ----- parse // region ----- parse
/** /**
* 对象转JSONObject对象 * 对象转{@link JSONObject}支持
* <ul>
* <li>{@link CharSequence}解析{...}字符串</li>
* <li>Bean解析</li>
* <li>Map解析</li>
* </ul>
* *
* @param obj Bean对象或者Map * @param obj Bean对象或者Map
* @return JSONObject * @return JSONObject
@ -292,23 +297,37 @@ public class JSONFactory {
} }
/** /**
* 转换对象为JSON如果用户不配置JSONConfig则JSON的有序与否与传入对象有关<br> * 解析对象为JSON<br>
* 支持的对象 * 支持的对象
* <ul> * <ul>
* <li>String: 转换为相应的对象</li> * <li>String: 转换为相应的对象</li>
* <li>ArrayIterableIterator转换为JSONArray</li> * <li>ArrayIterableIterator转换为JSONArray</li>
* <li>Bean对象转为JSONObject</li> * <li>Bean对象转为JSONObject</li>
* </ul> * </ul>
* 注意{@link #toJSON(Object)}不同的是对象如果为字符串会被当作json字符串解析
* *
* @param obj 对象 * @param obj 对象
* @return JSONJSONObject or JSONArray * @return JSONJSONObject or JSONArray
*/ */
public JSON parse(final Object obj) { public JSON parse(final Object obj) {
final JSONMapper mapper = this.getMapper(); return getMapper().toJSON(obj, true);
if (obj instanceof CharSequence) { }
return mapper.toJSON((CharSequence) obj);
} /**
return mapper.toJSON(obj); * 转换对象为JSON<br>
* 支持的对象
* <ul>
* <li>String: 转换为{@link JSONPrimitive}</li>
* <li>ArrayIterableIterator转换为JSONArray</li>
* <li>Bean对象转为JSONObject</li>
* </ul>
* 注意{@link #parse(Object)}不同的是对象如果为字符串则返回{@link JSONPrimitive}不会做二次解析
*
* @param obj 对象
* @return JSONJSONObject or JSONArray
*/
public JSON toJSON(final Object obj) {
return getMapper().toJSON(obj, false);
} }
// endregion // endregion

View File

@ -233,7 +233,8 @@ public class JSONObject extends MapWrapper<String, JSON> implements JSON, JSONGe
* @throws JSONException 值是无穷数字抛出此异常 * @throws JSONException 值是无穷数字抛出此异常
*/ */
public JSONObject putObj(final String key, final Object value) throws JSONException { public JSONObject putObj(final String key, final Object value) throws JSONException {
this.put(key, factory.getMapper().toJSON(value)); // put时如果value为字符串不解析而是作为JSONPrimitive对待
this.put(key, factory.getMapper().toJSON(value, false));
return this; return this;
} }

View File

@ -172,8 +172,8 @@ public class JSONUtil {
/** /**
* JSON字符串转JSONArray * JSON字符串转JSONArray
* *
* @param obj 数组或集合对象 * @param obj 数组或集合对象
* @param config JSON配置 * @param config JSON配置
* @return JSONArray * @return JSONArray
* @since 5.3.1 * @since 5.3.1
*/ */
@ -184,9 +184,9 @@ public class JSONUtil {
/** /**
* 对象转JSONArray * 对象转JSONArray
* *
* @param obj 数组或集合对象 * @param obj 数组或集合对象
* @param config JSON配置 * @param config JSON配置
* @param predicate index和值对过滤编辑器可以通过实现此接口完成解析前对键值对的过滤和修改操作{@link Predicate#test(Object)}{@code true}保留 * @param predicate index和值对过滤编辑器可以通过实现此接口完成解析前对键值对的过滤和修改操作{@link Predicate#test(Object)}{@code true}保留
* @return JSONArray * @return JSONArray
* @since 5.3.1 * @since 5.3.1
*/ */
@ -195,12 +195,12 @@ public class JSONUtil {
} }
/** /**
* 转换对象为JSON如果用户不配置JSONConfig则JSON的有序与否与传入对象有关<br> * 解析对象为JSON持的对象
* 支持的对象
* <ul> * <ul>
* <li>String: 转换为相应的对象</li> * <li>String: 解析为相应的对象</li>
* <li>ArrayIterableIterator转换为JSONArray</li> * <li>Numberboolean: 转换为{@link JSONPrimitive}</li>
* <li>Bean对象转为JSONObject</li> * <li>ArrayIterableIterator转换为{@link JSONArray}</li>
* <li>Bean对象转为{@link JSONObject}</li>
* </ul> * </ul>
* *
* @param obj 对象 * @param obj 对象
@ -211,45 +211,46 @@ public class JSONUtil {
} }
/** /**
* 转换对象为JSON如果用户不配置JSONConfig则JSON的有序与否与传入对象有关<br> * 解析对象为JSON持的对象
* 支持的对象
* <ul> * <ul>
* <li>String: 转换为相应的对象</li> * <li>String: 解析为相应的对象</li>
* <li>ArrayIterableIterator转换为JSONArray</li> * <li>Numberboolean: 转换为{@link JSONPrimitive}</li>
* <li>Bean对象转为JSONObject</li> * <li>ArrayIterableIterator转换为{@link JSONArray}</li>
* <li>Bean对象转为{@link JSONObject}</li>
* </ul> * </ul>
* *
* @param obj 对象 * @param obj 对象
* @param config JSON配置{@code null}使用默认配置 * @param config JSON配置{@code null}使用默认配置
* @return JSONJSONObject or JSONArray * @return JSON
*/ */
public static JSON parse(final Object obj, final JSONConfig config) { public static JSON parse(final Object obj, final JSONConfig config) {
return parse(obj, config, null); return parse(obj, config, null);
} }
/** /**
* 转换对象为JSON如果用户不配置JSONConfig则JSON的有序与否与传入对象有关<br> * 解析对象为JSON持的对象
* 支持的对象
* <ul> * <ul>
* <li>String: 转换为相应的对象</li> * <li>String: 解析为相应的对象</li>
* <li>ArrayIterableIterator转换为JSONArray</li> * <li>Numberboolean: 转换为{@link JSONPrimitive}</li>
* <li>Bean对象转为JSONObject</li> * <li>ArrayIterableIterator转换为{@link JSONArray}</li>
* <li>Bean对象转为{@link JSONObject}</li>
* </ul> * </ul>
* *
* @param obj 对象 * @param obj 对象
* @param config JSON配置{@code null}使用默认配置 * @param config JSON配置{@code null}使用默认配置
* @param predicate 键值对过滤编辑器可以通过实现此接口完成解析前对键值对的过滤和修改操作{@link Predicate#test(Object)}{@code true}保留 * @param predicate 键值对过滤编辑器可以通过实现此接口完成解析前对键值对的过滤和修改操作
* @return JSONJSONObject or JSONArray * {@link Predicate#test(Object)}{@code true}保留
* @return JSON
*/ */
public static JSON parse(final Object obj, final JSONConfig config, final Predicate<MutableEntry<Object, Object>> predicate) { public static JSON parse(final Object obj, final JSONConfig config, final Predicate<MutableEntry<Object, Object>> predicate) {
return JSONFactory.of(config, predicate).parse(obj); return JSONFactory.of(config, predicate).parse(obj);
} }
/** /**
* XML字符串转为JSONObject * XML字符串解析为{@link JSONObject}
* *
* @param xmlStr XML字符串 * @param xmlStr XML字符串
* @return JSONObject * @return {@link JSONObject}
*/ */
public static JSONObject parseFromXml(final String xmlStr) { public static JSONObject parseFromXml(final String xmlStr) {
return JSONXMLUtil.toJSONObject(xmlStr); return JSONXMLUtil.toJSONObject(xmlStr);
@ -335,7 +336,7 @@ public class JSONUtil {
/** /**
* 转换为JSON字符串并写出到writer * 转换为JSON字符串并写出到writer
* *
* @param obj 被转为JSON的对象 * @param obj 被转为JSON的对象
* @param appendable {@link Appendable} * @param appendable {@link Appendable}
* @since 5.3.3 * @since 5.3.3
*/ */
@ -389,7 +390,7 @@ public class JSONUtil {
* 转为实体类对象 * 转为实体类对象
* *
* @param <T> Bean类型 * @param <T> Bean类型
* @param obj 对象 * @param obj 对象
* @param type 实体类对象类型 * @param type 实体类对象类型
* @return 实体类对象 * @return 实体类对象
*/ */
@ -418,7 +419,62 @@ public class JSONUtil {
} }
// endregion // endregion
// region ----- toJSON
/**
* 转换对象为JSON如果用户不配置JSONConfig则JSON的有序与否与传入对象有关<br>
* 支持的对象
* <ul>
* <li>booleanNumberString: 转换为{@link JSONPrimitive}/li>
* <li>ArrayIterableIterator转换为{@link JSONArray}</li>
* <li>Bean对象转为{@link JSONObject}</li>
* </ul>
*
* @param obj 对象
* @return JSON
*/
public static JSON toJSON(final Object obj) {
return JSONFactory.getInstance().toJSON(obj);
}
/**
* 转换对象为JSON如果用户不配置JSONConfig则JSON的有序与否与传入对象有关<br>
* 支持的对象
* <ul>
* <li>booleanNumberString: 转换为{@link JSONPrimitive}</li>
* <li>ArrayIterableIterator转换为{@link JSONArray}</li>
* <li>Bean对象转为{@link JSONObject}</li>
* </ul>
*
* @param obj 对象
* @param config JSON配置{@code null}使用默认配置
* @return JSON
*/
public static JSON toJSON(final Object obj, final JSONConfig config) {
return toJSON(obj, config, null);
}
/**
* 转换对象为JSON如果用户不配置JSONConfig则JSON的有序与否与传入对象有关<br>
* 支持的对象
* <ul>
* <li>booleanNumberString: 转换为{@link JSONPrimitive}</li>
* <li>ArrayIterableIterator转换为{@link JSONArray}</li>
* <li>Bean对象转为{@link JSONObject}</li>
* </ul>
*
* @param obj 对象
* @param config JSON配置{@code null}使用默认配置
* @param predicate 键值对过滤编辑器可以通过实现此接口完成解析前对键值对的过滤和修改操作{@link Predicate#test(Object)}{@code true}保留
* @return JSON
*/
public static JSON toJSON(final Object obj, final JSONConfig config, final Predicate<MutableEntry<Object, Object>> predicate) {
return JSONFactory.of(config, predicate).toJSON(obj);
}
// endregion
// region ----- toList // region ----- toList
/** /**
* 将JSONArray字符串转换为Bean的List默认为ArrayList * 将JSONArray字符串转换为Bean的List默认为ArrayList
* *
@ -601,6 +657,7 @@ public class JSONUtil {
} }
// region ----- isType // region ----- isType
/** /**
* 是否为JSON类型字符串首尾都为大括号或中括号判定为JSON字符串 * 是否为JSON类型字符串首尾都为大括号或中括号判定为JSON字符串
* *
@ -642,6 +699,7 @@ public class JSONUtil {
// endregion // endregion
// region ----- registerTypeAdapter // region ----- registerTypeAdapter
/** /**
* 全局注册自定义类型适配器用于自定义对象序列化和反序列化 * 全局注册自定义类型适配器用于自定义对象序列化和反序列化
* *

View File

@ -20,6 +20,7 @@ import org.dromara.hutool.core.lang.Opt;
import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.*; import org.dromara.hutool.json.*;
import org.dromara.hutool.json.serializer.impl.CharSequenceTypeAdapter;
import org.dromara.hutool.json.serializer.impl.DefaultDeserializer; import org.dromara.hutool.json.serializer.impl.DefaultDeserializer;
import java.io.Serializable; import java.io.Serializable;
@ -29,7 +30,7 @@ import java.util.Optional;
/** /**
* 对象和JSON值映射器用于Java对象和JSON对象互转<br> * 对象和JSON值映射器用于Java对象和JSON对象互转<br>
* <ul> * <ul>
* <li>Java对象转JSON{@link #toJSON(Object)}</li> * <li>Java对象转JSON{@link #toJSON(Object, boolean)}</li>
* <li>JSON转Java对象{@link #toBean(JSON, Type)}</li> * <li>JSON转Java对象{@link #toBean(JSON, Type)}</li>
* </ul> * </ul>
* <p> * <p>
@ -139,22 +140,23 @@ public class JSONMapper implements Serializable {
} }
// region ----- toJSON // region ----- toJSON
/** /**
* 在需要的时候转换映射对象<br> * 在需要的时候转换映射对象<br>
* 包装包括 * 包装包括
* <ul> * <ul>
* <li>array or collection = JSONArray</li> * <li>array or collection = {@link JSONArray}</li>
* <li>map = JSONObject</li> * <li>map or bean = {@link JSONObject}</li>
* <li>standard property (Double, String, et al) = 原对象</li> * <li>standard property (number boolean or char) = {@link JSONPrimitive}</li>
* <li>String = parseIfString为{@code true}时解析为{@link JSON}{@code false}直接包装为{@link JSONPrimitive}</li>
* <li>其它 = 尝试包装为JSONObject否则返回{@code null}</li> * <li>其它 = 尝试包装为JSONObject否则返回{@code null}</li>
* </ul> * </ul>
* *
* @param obj 被映射的对象 * @param obj 被映射的对象
* @param parseIfString 如果提供的是字符串是否解析为JSON{@code false}则直接包装为{@link JSONPrimitive}
* @return 映射后的值null表示此值需被忽略 * @return 映射后的值null表示此值需被忽略
*/ */
public JSON toJSON(final Object obj) { public JSON toJSON(final Object obj, final boolean parseIfString) {
return mapTo(obj, null); return mapTo(obj, null, parseIfString);
} }
/** /**
@ -169,7 +171,7 @@ public class JSONMapper implements Serializable {
* @return 映射后的值null表示此值需被忽略 * @return 映射后的值null表示此值需被忽略
*/ */
public JSONObject toJSONObject(final Object obj) { public JSONObject toJSONObject(final Object obj) {
return mapTo(obj, factory.ofObj()); return mapTo(obj, factory.ofObj(), false);
} }
/** /**
@ -183,7 +185,7 @@ public class JSONMapper implements Serializable {
* @return 映射后的值null表示此值需被忽略 * @return 映射后的值null表示此值需被忽略
*/ */
public JSONArray toJSONArray(final Object obj) { public JSONArray toJSONArray(final Object obj) {
return mapTo(obj, factory.ofArray()); return mapTo(obj, factory.ofArray(), false);
} }
// endregion // endregion
@ -191,19 +193,22 @@ public class JSONMapper implements Serializable {
* 在需要的时候转换映射对象<br> * 在需要的时候转换映射对象<br>
* 包装包括 * 包装包括
* <ul> * <ul>
* <li>array or collection = JSONArray</li> * <li>array or collection = {@link JSONArray}</li>
* <li>map = JSONObject</li> * <li>map or bean = {@link JSONObject}</li>
* <li>standard property (Double, String, et al) = 原对象</li> * <li>standard property (number boolean or char) = {@link JSONPrimitive}</li>
* <li>String = parseIfString为{@code true}时解析为{@link JSON}{@code false}直接包装为{@link JSONPrimitive}</li>
* <li>其它 = 尝试包装为JSONObject否则返回{@code null}</li> * <li>其它 = 尝试包装为JSONObject否则返回{@code null}</li>
* </ul> * </ul>
* *
* @param obj 被映射的对象 * @param obj 被映射的对象
* @param json 被映射的到的对象{@code null}表示根据序列化器自动识别 * @param json 被映射的到的对象{@code null}表示根据序列化器自动识别
* @param <T> JSON类型 * @param parseIfString 如果提供的是字符串是否解析为JSON{@code false}则直接包装为{@link JSONPrimitive},
* 只有json参数为{@code null}时有效
* @param <T> JSON类型
* @return 映射后的值null表示此值需被忽略 * @return 映射后的值null表示此值需被忽略
*/ */
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
private <T extends JSON> T mapTo(Object obj, final T json) { private <T extends JSON> T mapTo(Object obj, final T json, final boolean parseIfString) {
if (null == obj) { if (null == obj) {
return null; return null;
} }
@ -224,9 +229,19 @@ public class JSONMapper implements Serializable {
// 考虑性能问题默认原始类型对象直接包装为JSONPrimitive不再查找TypeAdapter // 考虑性能问题默认原始类型对象直接包装为JSONPrimitive不再查找TypeAdapter
// 如果原始类型想转为其他JSON类型依旧可以查找TypeAdapter // 如果原始类型想转为其他JSON类型依旧可以查找TypeAdapter
if (JSONPrimitive.isTypeForJSONPrimitive(obj)) { if (JSONPrimitive.isTypeForJSONPrimitive(obj)) {
if (null == json || json instanceof JSONPrimitive) { if (null == json) {
// 未指定转换的JSON类型对于String产生二义性
// 通过parseIfString参数决定是解析字符串还是直接转为原始类型
if (parseIfString && obj instanceof String) {
return (T) CharSequenceTypeAdapter.INSTANCE.serialize((String)obj,
new SimpleJSONContext(null, factory));
}
return (T) factory.ofPrimitive(obj); return (T) factory.ofPrimitive(obj);
} }
if (json instanceof JSONPrimitive) {
return (T) factory.ofPrimitive(obj);
}
// 用户想将Primitive对象转为特定JSON对象则需要查找TypeAdapter
} }
// JSON对象如果与预期结果类型一致则直接返回 // JSON对象如果与预期结果类型一致则直接返回

View File

@ -19,7 +19,6 @@ package org.dromara.hutool.json.serializer.impl;
import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.json.JSON; import org.dromara.hutool.json.JSON;
import org.dromara.hutool.json.JSONFactory;
import org.dromara.hutool.json.JSONObject; import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.JSONPrimitive; import org.dromara.hutool.json.JSONPrimitive;
import org.dromara.hutool.json.reader.JSONTokener; import org.dromara.hutool.json.reader.JSONTokener;
@ -86,7 +85,7 @@ public class CharSequenceTypeAdapter implements MatcherJSONSerializer<CharSequen
} }
// 按照JSON字符串解析 // 按照JSON字符串解析
return parse(new JSONTokener(jsonStr), context.getFactory()); return context.getFactory().ofParser(new JSONTokener(jsonStr)).parse();
} }
@Override @Override
@ -96,15 +95,4 @@ public class CharSequenceTypeAdapter implements MatcherJSONSerializer<CharSequen
} }
return json.toString(); return json.toString();
} }
/**
* {@link JSONTokener} 中读取JSON字符串并解析为JSON
*
* @param tokener {@link JSONTokener}
* @param factory JSON工厂
* @return JSON
*/
public static JSON parse(final JSONTokener tokener, final JSONFactory factory) {
return factory.ofParser(tokener).parse();
}
} }

View File

@ -0,0 +1,36 @@
/*
* 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;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class JSONFactoryTest {
@Test
void parseFromStringBuilderTest() {
final String jsonStr = "{\"name\":\"张三\"}";
final JSON parse = JSONFactory.getInstance().parse(new StringBuilder(jsonStr));
Assertions.assertEquals(JSONObject.class, parse.getClass());
}
@Test
void parseFromStringTest() {
final String jsonStr = "{\"name\":\"张三\"}";
final JSON parse = JSONFactory.getInstance().parse(jsonStr);
Assertions.assertEquals(JSONObject.class, parse.getClass());
}
}

View File

@ -31,7 +31,7 @@ public class Issue3681Test {
Assertions.assertEquals("\"abc\"", abc); Assertions.assertEquals("\"abc\"", abc);
abc = JSONUtil.toJsonStr(Optional.of("123")); abc = JSONUtil.toJsonStr(Optional.of("123"));
Assertions.assertEquals("\"123\"", abc); Assertions.assertEquals("123", abc);
} }
@Test @Test
@ -46,6 +46,7 @@ public class Issue3681Test {
Assertions.assertEquals("\"abc\"", abc); Assertions.assertEquals("\"abc\"", abc);
abc = JSONUtil.toJsonStr(Opt.of("123")); abc = JSONUtil.toJsonStr(Opt.of("123"));
Assertions.assertEquals("\"123\"", abc); Assertions.assertEquals("123", abc);
} }
} }

View File

@ -33,7 +33,7 @@ public class IssueI9DX5HTest {
entry.setKey(StrUtil.toUnderlineCase((CharSequence) entry.getKey())); entry.setKey(StrUtil.toUnderlineCase((CharSequence) entry.getKey()));
return true; return true;
}); });
final JSONObject jsonObject = (JSONObject) factory.parse(xml); final JSONObject jsonObject = factory.parseObj(xml);
Assertions.assertEquals("{\"good_msg\":\"你好\"}", jsonObject.toString()); Assertions.assertEquals("{\"good_msg\":\"你好\"}", jsonObject.toString());
} }