修改JSON的过滤接口,实现多层过滤

This commit is contained in:
Looly
2022-08-30 21:35:00 +08:00
parent f4fc7793eb
commit 0590793775
11 changed files with 330 additions and 116 deletions

View File

@@ -1,12 +1,14 @@
package cn.hutool.json;
import cn.hutool.core.bean.BeanPath;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.json.convert.JSONConverterOld;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.function.Predicate;
/**
* JSON接口
@@ -23,6 +25,13 @@ public interface JSON extends Cloneable, Serializable {
*/
JSONConfig getConfig();
/**
* JSON大小对于JSONObject是键值对的多少JSONArray则是元素的个数
*
* @return 大小
*/
int size();
/**
* 通过表达式获取JSON中嵌套的对象<br>
* <ol>
@@ -121,7 +130,7 @@ public interface JSON extends Cloneable, Serializable {
default String toJSONString(final int indentFactor) throws JSONException {
final StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
return this.write(sw, indentFactor, 0).toString();
return this.write(sw, indentFactor, 0, null).toString();
}
}
@@ -134,7 +143,7 @@ public interface JSON extends Cloneable, Serializable {
* @throws JSONException JSON相关异常
*/
default Writer write(final Writer writer) throws JSONException {
return this.write(writer, 0, 0);
return this.write(writer, 0, 0, null);
}
/**
@@ -144,10 +153,11 @@ public interface JSON extends Cloneable, Serializable {
* @param writer writer
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
* @param indent 本级别缩进量
* @param predicate 过滤器可以修改值keyindex无法修改{@link Predicate#test(Object)}为{@code true}保留
* @return Writer
* @throws JSONException JSON相关异常
*/
Writer write(Writer writer, int indentFactor, int indent) throws JSONException;
Writer write(Writer writer, int indentFactor, int indent, final Predicate<MutableEntry<Object, Object>> predicate) throws JSONException;
/**
* 转为实体类对象,转换异常将被抛出

View File

@@ -533,7 +533,7 @@ public class JSONArray implements JSON, JSONGetter<Integer>, List<Object>, Rando
* @return JSON字符串
* @since 5.7.15
*/
public String toJSONString(final int indentFactor, final Predicate<MutableEntry<Integer, Object>> predicate) {
public String toJSONString(final int indentFactor, final Predicate<MutableEntry<Object, Object>> predicate) {
final StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
return this.write(sw, indentFactor, 0, predicate).toString();
@@ -541,32 +541,10 @@ public class JSONArray implements JSON, JSONGetter<Integer>, List<Object>, Rando
}
@Override
public Writer write(final Writer writer, final int indentFactor, final int indent) throws JSONException {
return write(writer, indentFactor, indent, null);
}
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).beginArray();
/**
* 将JSON内容写入Writer<br>
* 支持过滤器,即选择哪些字段或值不写出
*
* @param writer writer
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
* @param indent 本级别缩进量
* @param predicate 过滤器可以修改值keyindex无法修改{@link Predicate#test(Object)}为{@code true}保留
* @return Writer
* @throws JSONException JSON相关异常
* @since 5.7.15
*/
public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate<MutableEntry<Integer, Object>> predicate) throws JSONException {
final JSONWriter jsonWriter = JSONWriter.of(writer, indentFactor, indent, config)
.beginArray();
CollUtil.forEach(this, (value, index) -> {
final MutableEntry<Integer, Object> pair = new MutableEntry<>(index, value);
if (null == predicate || predicate.test(pair)) {
jsonWriter.writeValue(pair.getValue());
}
});
CollUtil.forEach(this, (value, index) -> jsonWriter.writeField(new MutableEntry<>(index, value), predicate));
jsonWriter.end();
// 此处不关闭Writer考虑writer后续还需要填内容
return writer;

View File

@@ -341,18 +341,13 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
* @return JSON字符串
* @since 5.7.15
*/
public String toJSONString(final int indentFactor, final Predicate<MutableEntry<String, Object>> predicate) {
public String toJSONString(final int indentFactor, final Predicate<MutableEntry<Object, Object>> predicate) {
final StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
return this.write(sw, indentFactor, 0, predicate).toString();
}
}
@Override
public Writer write(final Writer writer, final int indentFactor, final int indent) throws JSONException {
return write(writer, indentFactor, indent, null);
}
/**
* 将JSON内容写入Writer<br>
* 支持过滤器,即选择哪些字段或值不写出
@@ -365,20 +360,11 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
* @throws JSONException JSON相关异常
* @since 5.7.15
*/
public Writer write(final Writer writer, final int indentFactor, final int indent, final Predicate<MutableEntry<String, Object>> predicate) throws JSONException {
@Override
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)
.beginObj();
this.forEach((key, value) -> {
if (null != predicate) {
final MutableEntry<String, Object> pair = new MutableEntry<>(key, value);
if (predicate.test(pair)) {
// 使用修改后的键值对
jsonWriter.writeField(pair.getKey(), pair.getValue());
}
} else {
jsonWriter.writeField(key, value);
}
});
this.forEach((key, value) -> jsonWriter.writeField(new MutableEntry<>(key, value), predicate));
jsonWriter.end();
// 此处不关闭Writer考虑writer后续还需要填内容
return writer;

View File

@@ -530,32 +530,6 @@ public class JSONUtil {
return (T) json.getByPath(expression);
}
/**
* 设置表达式指定位置或filed对应的值<br>
* 若表达式指向一个JSONArray则设置其坐标对应位置的值若指向JSONObject则put对应key的值<br>
* 注意如果为JSONArray则设置值得下标不能大于已有JSONArray的长度<br>
* <ol>
* <li>.表达式可以获取Bean对象中的属性字段值或者Map中key对应的值</li>
* <li>[]表达式可以获取集合等对象中对应index的值</li>
* </ol>
* <p>
* 表达式栗子:
*
* <pre>
* persion
* persion.name
* persons[3]
* person.friends[5].name
* </pre>
*
* @param json JSON可以为JSONObject或JSONArray
* @param expression 表达式
* @param value 值
*/
public static void putByPath(final JSON json, final String expression, final Object value) {
json.putByPath(expression, value);
}
/**
* 格式化JSON字符串此方法并不严格检查JSON的格式正确与否
*

View File

@@ -23,6 +23,10 @@ public class AlgorithmUtil {
map.put("HS384", HmacAlgorithm.HmacSHA384.getValue());
map.put("HS512", HmacAlgorithm.HmacSHA512.getValue());
map.put("HMD5", HmacAlgorithm.HmacMD5.getValue());
map.put("HSHA1", HmacAlgorithm.HmacSHA1.getValue());
map.put("SM4CMAC", HmacAlgorithm.SM4CMAC.getValue());
map.put("RS256", SignAlgorithm.SHA256withRSA.getValue());
map.put("RS384", SignAlgorithm.SHA384withRSA.getValue());
map.put("RS512", SignAlgorithm.SHA512withRSA.getValue());
@@ -34,6 +38,14 @@ public class AlgorithmUtil {
map.put("PS256", SignAlgorithm.SHA256withRSA_PSS.getValue());
map.put("PS384", SignAlgorithm.SHA384withRSA_PSS.getValue());
map.put("PS512", SignAlgorithm.SHA512withRSA_PSS.getValue());
map.put("RMD2", SignAlgorithm.MD2withRSA.getValue());
map.put("RMD5", SignAlgorithm.MD5withRSA.getValue());
map.put("RSHA1", SignAlgorithm.SHA1withRSA.getValue());
map.put("DNONE", SignAlgorithm.NONEwithDSA.getValue());
map.put("DSHA1", SignAlgorithm.SHA1withDSA.getValue());
map.put("ENONE", SignAlgorithm.NONEwithECDSA.getValue());
map.put("ESHA1", SignAlgorithm.SHA1withECDSA.getValue());
}
/**

View File

@@ -120,6 +120,106 @@ public class JWTSignerUtil {
return createSigner("ES512", key);
}
/**
* HMD5(HmacMD5)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner hmd5(final Key key) {
return createSigner("HMD5",key);
}
/**
* HSHA1(HmacSHA1)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner hsha1(final Key key) {
return createSigner("HSHA1",key);
}
/**
* SM4CMAC(SM4CMAC)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner sm4cmac(final Key key) {
return createSigner("SM4CMAC",key);
}
/**
* RMD2(MD2withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rmd2(final Key key) {
return createSigner("RMD2",key);
}
/**
* RMD5(MD5withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rmd5(final Key key) {
return createSigner("RMD5",key);
}
/**
* RSHA1(SHA1withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rsha1(final Key key) {
return createSigner("RSHA1",key);
}
/**
* DNONE(NONEwithDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner dnone(final Key key) {
return createSigner("DNONE",key);
}
/**
* DSHA1(SHA1withDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner dsha1(final Key key) {
return createSigner("DSHA1",key);
}
/**
* ENONE(NONEwithECDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner enone(final Key key) {
return createSigner("ENONE",key);
}
/**
* ESHA1(SHA1withECDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner esha1(final Key key) {
return createSigner("ESHA1",key);
}
/**
* 创建签名器
*

View File

@@ -5,6 +5,7 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TemporalAccessorUtil;
import cn.hutool.core.date.format.GlobalCustomFormat;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.math.NumberUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharUtil;
@@ -18,6 +19,7 @@ import java.io.Writer;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.function.Predicate;
/**
* JSON数据写出器<br>
@@ -123,54 +125,56 @@ public class JSONWriter extends Writer {
return this;
}
/**
* 写出字段名及字段值,如果字段值是{@code null}且忽略null值则不写出任何内容<br>
* 在{@link #arrayMode} 为 {@code true} 时key是数字此时不写出键只写值
*
* @param pair 键值对
* @param predicate 过滤修改器
* @return this
* @since 6.0.0
*/
@SuppressWarnings({"UnusedReturnValue", "resource"})
public JSONWriter writeField(final MutableEntry<Object, Object> pair, final Predicate<MutableEntry<Object, Object>> predicate) {
final Object value = pair.getValue();
if (null == value && config.isIgnoreNullValue()) {
return this;
}
if (null != predicate) {
if (false == predicate.test(pair)) {
// 使用修改后的键值对
return this;
}
}
final Object key = pair.getKey();
if(false == arrayMode){
// JSONObject模式写出键否则只输出值
writeKey(StrUtil.toString(key));
}
return writeValueDirect(value, predicate);
}
/**
* 写出键,自动处理分隔符和缩进,并包装键名
*
* @param key 键名
* @return this
*/
@SuppressWarnings({"resource", "UnusedReturnValue"})
public JSONWriter writeKey(final String key) {
if (needSeparator) {
//noinspection resource
writeRaw(CharUtil.COMMA);
}
// 换行缩进
//noinspection resource
writeLF().writeSpace(indentFactor + indent);
return writeRaw(InternalJSONUtil.quote(key));
}
/**
* 写出值,自动处理分隔符和缩进,自动判断类型,并根据不同类型写出特定格式的值<br>
* 如果写出的值为{@code null}且配置忽略null则跳过。
*
* @param value 值
* @return this
*/
public JSONWriter writeValue(final Object value) {
if (null == value && config.isIgnoreNullValue()) {
return this;
}
return writeValueDirect(value);
}
/**
* 写出字段名及字段值,如果字段值是{@code null}且忽略null值则不写出任何内容
*
* @param key 字段名
* @param value 字段值
* @return this
* @since 5.7.6
*/
public JSONWriter writeField(final String key, final Object value) {
if (null == value && config.isIgnoreNullValue()) {
return this;
}
//noinspection resource
return writeKey(key).writeValueDirect(value);
}
@SuppressWarnings({"SpellCheckingInspection", "NullableProblems"})
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
this.writer.write(cbuf, off, len);
@@ -196,9 +200,10 @@ public class JSONWriter extends Writer {
* 写出值,自动处理分隔符和缩进,自动判断类型,并根据不同类型写出特定格式的值
*
* @param value 值
* @param predicate 过滤修改器
* @return this
*/
private JSONWriter writeValueDirect(final Object value) {
private JSONWriter writeValueDirect(final Object value, final Predicate<MutableEntry<Object, Object>> predicate) {
if (arrayMode) {
if (needSeparator) {
//noinspection resource
@@ -212,22 +217,23 @@ public class JSONWriter extends Writer {
writeRaw(CharUtil.COLON).writeSpace(1);
}
needSeparator = true;
return writeObjValue(value);
return writeObjValue(value, predicate);
}
/**
* 写出JSON的值根据值类型不同输出不同内容
*
* @param value 值
* @param predicate 过滤修改器
* @return this
*/
private JSONWriter writeObjValue(final Object value) {
private JSONWriter writeObjValue(final Object value, final Predicate<MutableEntry<Object, Object>> predicate) {
final int indent = indentFactor + this.indent;
if (value == null) {
//noinspection resource
writeRaw(StrUtil.NULL);
} else if (value instanceof JSON) {
((JSON) value).write(writer, indentFactor, indent);
((JSON) value).write(writer, indentFactor, indent, predicate);
} else if (value instanceof Number) {
writeNumberValue((Number) value);
} else if (value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor) {

View File

@@ -0,0 +1,26 @@
package cn.hutool.json;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import org.junit.Assert;
import org.junit.Test;
/**
* Predicate多层过滤
*/
public class IssueI5OMSCTest {
@Test
public void filterTest(){
final JSONObject json = JSONUtil.parseObj(ResourceUtil.readUtf8Str("issueI5OMSC.json"));
final String s = json.toJSONString(0, (entry) -> {
final Object key = entry.getKey();
if(key instanceof String){
return ListUtil.of("store", "bicycle", "color", "book", "author").contains(key);
}
return true;
});
Assert.assertEquals("{\"store\":{\"bicycle\":{\"color\":\"red\"},\"book\":[{\"author\":\"Evelyn Waugh\"},{\"author\":\"Evelyn Waugh02\"}]}}", s);
}
}

View File

@@ -700,7 +700,7 @@ public class JSONObjectTest {
.set("d", true);
final String s = json1.toJSONString(0, (pair) -> {
pair.setKey(StrUtil.toUnderlineCase(pair.getKey()));
pair.setKey(StrUtil.toUnderlineCase((String)pair.getKey()));
return true;
});
Assert.assertEquals("{\"a_key\":\"value1\",\"b_job\":\"value2\",\"c_good\":\"value3\",\"d\":true}", s);

View File

@@ -116,6 +116,96 @@ public class JWTSignerTest {
signAndVerify(signer);
}
@Test
public void hmd5Test(){
final String id = "hmd5";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void hsha1Test(){
final String id = "hsha1";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void sm4cmacTest(){
final String id = "sm4cmac";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void rmd2Test(){
final String id = "rmd2";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void rmd5Test(){
final String id = "rmd5";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void rsha1Test(){
final String id = "rsha1";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void dnoneTest(){
final String id = "dnone";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void dsha1Test(){
final String id = "dsha1";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void enoneTest(){
final String id = "enone";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
@Test
public void esha1Test(){
final String id = "esha1";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
signAndVerify(signer);
}
private static void signAndVerify(final JWTSigner signer){
final JWT jwt = JWT.of()
.setPayload("sub", "1234567890")

View File

@@ -0,0 +1,32 @@
{
"store": {
"bicycle": {
"color": "red",
"price": 19.95
},
"book": [
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99,
"items": [
{
"name": "wujing001",
"age": 18
},
{
"name": "wujing002",
"age": 18
}
]
},
{
"category": "fiction02",
"author": "Evelyn Waugh02",
"title": "Sword of Honour02",
"price": 12.99
}
]
}
}