This commit is contained in:
Looly
2024-04-11 23:27:56 +08:00
parent a066afd4d4
commit 26dcf5fd3d
5 changed files with 99 additions and 25 deletions

View File

@@ -195,12 +195,13 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
/** /**
* 根据lambda的方法引用获取 * 根据lambda的方法引用获取
*
* @param func 方法引用 * @param func 方法引用
* @param <P> 参数类型 * @param <P> 参数类型
* @param <T> 返回值类型 * @param <T> 返回值类型
* @return 获取表达式对应属性和返回的对象 * @return 获取表达式对应属性和返回的对象
*/ */
public <P, T> T get(final SerFunction<P, T> func){ public <P, T> T get(final SerFunction<P, T> func) {
final LambdaInfo lambdaInfo = LambdaUtil.resolve(func); final LambdaInfo lambdaInfo = LambdaUtil.resolve(func);
return get(lambdaInfo.getFieldName(), lambdaInfo.getReturnType()); return get(lambdaInfo.getFieldName(), lambdaInfo.getReturnType());
} }
@@ -314,6 +315,38 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
* @throws JSONException 如果给定键为{@code null}或者键对应的值存在且为非JSONArray * @throws JSONException 如果给定键为{@code null}或者键对应的值存在且为非JSONArray
*/ */
public JSONObject append(final String key, final Object value) throws JSONException { public JSONObject append(final String key, final Object value) throws JSONException {
return append(key, value, null);
}
/**
* 追加值.
* <ul>
* <li>如果键值对不存在或对应值为{@code null}则value为单独值</li>
* <li>如果值是一个{@link JSONArray},追加之</li>
* <li>如果值是一个其他值,则和旧值共同组合为一个{@link JSONArray}</li>
* </ul>
*
* @param key 键
* @param value 值
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤,{@link Predicate#test(Object)}为{@code true}保留
* @return this.
* @throws JSONException 如果给定键为{@code null}或者键对应的值存在且为非JSONArray
* @since 6.0.0
*/
public JSONObject append(String key, Object value, final Predicate<MutableEntry<String, Object>> predicate) throws JSONException {
// 添加前置过滤通过MutablePair实现过滤、修改键值对等
if (null != predicate) {
final MutableEntry<String, Object> pair = new MutableEntry<>(key, value);
if (predicate.test(pair)) {
// 使用修改后的键值对
key = pair.getKey();
value = pair.getValue();
} else {
// 键值对被过滤
return this;
}
}
final Object object = this.getObj(key); final Object object = this.getObj(key);
if (object == null) { if (object == null) {
this.set(key, value); this.set(key, value);
@@ -396,7 +429,7 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
@Override @Override
public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate<MutableEntry<Object, Object>> predicate) throws JSONException { public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate<MutableEntry<Object, Object>> predicate) throws JSONException {
final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config) final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config)
.beginObj(); .beginObj();
this.forEach((key, value) -> jsonWriter.writeField(new MutableEntry<>(key, value), predicate)); this.forEach((key, value) -> jsonWriter.writeField(new MutableEntry<>(key, value), predicate));
jsonWriter.end(); jsonWriter.end();
// 此处不关闭Writer考虑writer后续还需要填内容 // 此处不关闭Writer考虑writer后续还需要填内容

View File

@@ -20,15 +20,10 @@ import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.lang.mutable.MutableEntry; import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.core.reflect.method.MethodUtil; import org.dromara.hutool.core.reflect.method.MethodUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.json.InternalJSONUtil; import org.dromara.hutool.json.*;
import org.dromara.hutool.json.JSONArray;
import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.JSONParser;
import org.dromara.hutool.json.JSONTokener;
import org.dromara.hutool.json.xml.JSONXMLUtil;
import org.dromara.hutool.json.serialize.GlobalSerializeMapping; import org.dromara.hutool.json.serialize.GlobalSerializeMapping;
import org.dromara.hutool.json.serialize.JSONSerializer; import org.dromara.hutool.json.serialize.JSONSerializer;
import org.dromara.hutool.json.xml.JSONXMLParser;
import org.dromara.hutool.json.xml.ParseConfig; import org.dromara.hutool.json.xml.ParseConfig;
import java.io.InputStream; import java.io.InputStream;
@@ -171,7 +166,8 @@ public class JSONObjectMapper {
final String jsonStr = StrUtil.trim(source); final String jsonStr = StrUtil.trim(source);
if (StrUtil.startWith(jsonStr, '<')) { if (StrUtil.startWith(jsonStr, '<')) {
// 可能为XML // 可能为XML
JSONXMLUtil.toJSONObject(jsonStr, jsonObject, ParseConfig.of()); //JSONXMLUtil.toJSONObject(jsonStr, jsonObject, ParseConfig.of());
JSONXMLParser.of(ParseConfig.of(), this.predicate).parseJSONObject(jsonStr, jsonObject);
return; return;
} }
mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonObject.config()), jsonObject); mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonObject.config()), jsonObject);

View File

@@ -12,6 +12,7 @@
package org.dromara.hutool.json.xml; package org.dromara.hutool.json.xml;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.xml.XmlConstants; import org.dromara.hutool.core.xml.XmlConstants;
@@ -19,6 +20,8 @@ import org.dromara.hutool.json.JSONException;
import org.dromara.hutool.json.JSONObject; import org.dromara.hutool.json.JSONObject;
import org.dromara.hutool.json.mapper.JSONValueMapper; import org.dromara.hutool.json.mapper.JSONValueMapper;
import java.util.function.Predicate;
/** /**
* XML解析器将XML解析为JSON对象 * XML解析器将XML解析为JSON对象
* *
@@ -27,19 +30,43 @@ import org.dromara.hutool.json.mapper.JSONValueMapper;
*/ */
public class JSONXMLParser { public class JSONXMLParser {
/**
* 创建XML解析器
*
* @param parseConfig 解析选项
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留
* @return JSONXMLParser
*/
public static JSONXMLParser of(final ParseConfig parseConfig, final Predicate<MutableEntry<String, Object>> predicate) {
return new JSONXMLParser(parseConfig, predicate);
}
private final ParseConfig parseConfig;
private final Predicate<MutableEntry<String, Object>> predicate;
/**
* 构造
*
* @param parseConfig 解析选项
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@link Predicate#test(Object)}为{@code true}保留
*/
public JSONXMLParser(final ParseConfig parseConfig, final Predicate<MutableEntry<String, Object>> predicate) {
this.parseConfig = parseConfig;
this.predicate = predicate;
}
/** /**
* 转换XML为JSONObject * 转换XML为JSONObject
* 转换过程中一些信息可能会丢失JSON中无法区分节点和属性相同的节点将被处理为JSONArray。 * 转换过程中一些信息可能会丢失JSON中无法区分节点和属性相同的节点将被处理为JSONArray。
* *
* @param xmlStr XML字符串 * @param xmlStr XML字符串
* @param jo JSONObject * @param jo JSONObject
* @param parseConfig 解析选项
* @throws JSONException 解析异常 * @throws JSONException 解析异常
*/ */
public static void parseJSONObject(final String xmlStr, final JSONObject jo, final ParseConfig parseConfig) throws JSONException { public void parseJSONObject(final String xmlStr, final JSONObject jo) throws JSONException {
final XMLTokener x = new XMLTokener(xmlStr, jo.config()); final XMLTokener x = new XMLTokener(xmlStr, jo.config());
while (x.more() && x.skipPast("<")) { while (x.more() && x.skipPast("<")) {
parse(x, jo, null, parseConfig, 0); parse(x, jo, null, 0);
} }
} }
@@ -49,13 +76,11 @@ public class JSONXMLParser {
* @param x {@link XMLTokener} * @param x {@link XMLTokener}
* @param context {@link JSONObject} * @param context {@link JSONObject}
* @param name 标签名null表示从根标签开始解析 * @param name 标签名null表示从根标签开始解析
* @param parseConfig 解析选项
* @param currentNestingDepth 当前层级 * @param currentNestingDepth 当前层级
* @return {@code true}表示解析完成 * @return {@code true}表示解析完成
* @throws JSONException JSON异常 * @throws JSONException JSON异常
*/ */
private static boolean parse(final XMLTokener x, final JSONObject context, final String name, private boolean parse(final XMLTokener x, final JSONObject context, final String name, final int currentNestingDepth) throws JSONException {
final ParseConfig parseConfig, final int currentNestingDepth) throws JSONException {
final char c; final char c;
int i; int i;
final JSONObject jsonobject; final JSONObject jsonobject;
@@ -155,9 +180,9 @@ public class JSONXMLParser {
throw x.syntaxError("Misshaped tag"); throw x.syntaxError("Misshaped tag");
} }
if (!jsonobject.isEmpty()) { if (!jsonobject.isEmpty()) {
context.append(tagName, jsonobject); context.append(tagName, jsonobject, this.predicate);
} else { } else {
context.append(tagName, StrUtil.EMPTY); context.append(tagName, StrUtil.EMPTY, this.predicate);
} }
return false; return false;
@@ -185,13 +210,13 @@ public class JSONXMLParser {
} }
// Nested element // Nested element
if (parse(x, jsonobject, tagName, parseConfig, currentNestingDepth + 1)) { if (parse(x, jsonobject, tagName, currentNestingDepth + 1)) {
if (jsonobject.isEmpty()) { if (jsonobject.isEmpty()) {
context.append(tagName, StrUtil.EMPTY); context.append(tagName, StrUtil.EMPTY, this.predicate);
} else if (jsonobject.size() == 1 && jsonobject.get("content") != null) { } else if (jsonobject.size() == 1 && jsonobject.get("content") != null) {
context.append(tagName, jsonobject.get("content")); context.append(tagName, jsonobject.get("content"), this.predicate);
} else { } else {
context.append(tagName, jsonobject); context.append(tagName, jsonobject, this.predicate);
} }
return false; return false;
} }

View File

@@ -64,7 +64,7 @@ public class JSONXMLUtil {
* @since 5.3.1 * @since 5.3.1
*/ */
public static JSONObject toJSONObject(final String xmlStr, final JSONObject jo, final ParseConfig parseConfig) throws JSONException { public static JSONObject toJSONObject(final String xmlStr, final JSONObject jo, final ParseConfig parseConfig) throws JSONException {
JSONXMLParser.parseJSONObject(xmlStr, jo, parseConfig); JSONXMLParser.of(parseConfig, null).parseJSONObject(xmlStr, jo);
return jo; return jo;
} }

View File

@@ -0,0 +1,20 @@
package org.dromara.hutool.json;
import org.dromara.hutool.core.text.StrUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class IssueI9DX5HTest {
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
@Test
void xmlToJSONTest() {
final String xml = "<GoodMsg>你好</GoodMsg>";
final JSONObject jsonObject = new JSONObject(xml, JSONConfig.of(), entry -> {
entry.setKey(StrUtil.toUnderlineCase(entry.getKey()));
return true;
});
Assertions.assertEquals("{\"good_msg\":\"你好\"}", jsonObject.toString());
}
}