From c8ba21dac277d316f62486f98c0e3f94a9f7459f Mon Sep 17 00:00:00 2001 From: redkale Date: Mon, 16 Sep 2024 22:30:25 +0800 Subject: [PATCH] JsonCharsWriter --- .../convert/json/JsonByteBufferWriter.java | 26 +- .../redkale/convert/json/JsonBytesWriter.java | 66 ++- .../redkale/convert/json/JsonCharsWriter.java | 554 ++++++++++++++++++ .../org/redkale/convert/json/JsonConvert.java | 22 +- .../redkale/convert/json/JsonDynEncoder.java | 72 ++- .../org/redkale/convert/json/JsonWriter.java | 46 +- .../test/convert/StringWrapperTest.java | 2 +- 7 files changed, 720 insertions(+), 68 deletions(-) create mode 100644 src/main/java/org/redkale/convert/json/JsonCharsWriter.java diff --git a/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java b/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java index 9c750746c..2a772cb87 100644 --- a/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java @@ -348,7 +348,7 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override - public void writeFieldShortValue(final byte[] fieldBytes, final short value) { + public void writeFieldShortValue(final byte[] fieldBytes, final char[] fieldChars, final short value) { byte[] bs1 = fieldBytes; byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); int expandsize = expand(bs1.length + bs2.length); @@ -374,7 +374,7 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override - public void writeFieldIntValue(final byte[] fieldBytes, final int value) { + public void writeFieldIntValue(final byte[] fieldBytes, final char[] fieldChars, final int value) { byte[] bs1 = fieldBytes; byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); int expandsize = expand(bs1.length + bs2.length); @@ -400,7 +400,7 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override - public void writeFieldLongValue(final byte[] fieldBytes, final long value) { + public void writeFieldLongValue(final byte[] fieldBytes, final char[] fieldChars, final long value) { byte[] bs1 = fieldBytes; byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); int expandsize = expand(bs1.length + bs2.length); @@ -426,7 +426,7 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override - public void writeFieldLatin1Value(final byte[] fieldBytes, final String value) { + public void writeFieldLatin1Value(final byte[] fieldBytes, final char[] fieldChars, final String value) { byte[] bs1 = fieldBytes; byte[] bs2 = Utility.latin1ByteArray(value); int expandsize = expand(bs1.length + bs2.length + 2); @@ -466,7 +466,7 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override - public void writeLastFieldShortValue(final byte[] fieldBytes, final short value) { + public void writeLastFieldShortValue(final byte[] fieldBytes, final char[] fieldChars, final short value) { byte[] bs1 = fieldBytes; byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); int expandsize = expand(bs1.length + bs2.length + 1); @@ -499,7 +499,7 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override - public void writeLastFieldIntValue(final byte[] fieldBytes, final int value) { + public void writeLastFieldIntValue(final byte[] fieldBytes, final char[] fieldChars, final int value) { byte[] bs1 = fieldBytes; byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); int expandsize = expand(bs1.length + bs2.length + 1); @@ -532,7 +532,7 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override - public void writeLastFieldLongValue(final byte[] fieldBytes, final long value) { + public void writeLastFieldLongValue(final byte[] fieldBytes, final char[] fieldChars, final long value) { byte[] bs1 = fieldBytes; byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); int expandsize = expand(bs1.length + bs2.length + 1); @@ -565,7 +565,7 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override - public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) { + public void writeLastFieldLatin1Value(final byte[] fieldBytes, final char[] fieldChars, final String value) { if (value == null && nullable()) { writeTo(fieldBytes); writeNull(); @@ -623,7 +623,8 @@ public class JsonByteBufferWriter extends JsonWriter { } @Override // firstFieldBytes 必须带{开头 - public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) { + public void writeObjectByOnlyOneLatin1FieldValue( + final byte[] firstFieldBytes, final char[] firstFieldChars, final String value) { if (value == null && nullable()) { writeTo('{'); writeTo(firstFieldBytes); @@ -692,7 +693,12 @@ public class JsonByteBufferWriter extends JsonWriter { @Override // firstFieldBytes 必须带{开头, lastFieldBytes必须,开头 public void writeObjectByOnlyTwoIntFieldValue( - final byte[] firstFieldBytes, final int value1, final byte[] lastFieldBytes, final int value2) { + final byte[] firstFieldBytes, + final char[] firstFieldChars, + final int value1, + final byte[] lastFieldBytes, + final char[] lastFieldChars, + final int value2) { byte[] bs1 = firstFieldBytes; byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value1)); byte[] bs3 = lastFieldBytes; diff --git a/src/main/java/org/redkale/convert/json/JsonBytesWriter.java b/src/main/java/org/redkale/convert/json/JsonBytesWriter.java index f47440845..59a69506f 100644 --- a/src/main/java/org/redkale/convert/json/JsonBytesWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonBytesWriter.java @@ -31,17 +31,11 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { private static final int TENTHOUSAND_MAX = 10001; private static final byte[][] TENTHOUSAND_BYTES = new byte[TENTHOUSAND_MAX][]; + private static final byte[][] TENTHOUSAND_BYTES2 = new byte[TENTHOUSAND_MAX][]; static { for (int i = 0; i < TENTHOUSAND_BYTES.length; i++) { TENTHOUSAND_BYTES[i] = String.valueOf(i).getBytes(); - } - } - - private static final byte[][] TENTHOUSAND_BYTES2 = new byte[TENTHOUSAND_MAX][]; - - static { - for (int i = 0; i < TENTHOUSAND_BYTES2.length; i++) { TENTHOUSAND_BYTES2[i] = String.valueOf(-i).getBytes(); } } @@ -191,7 +185,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override - public void writeFieldShortValue(final byte[] fieldBytes, final short value) { + public void writeFieldShortValue(final byte[] fieldBytes, final char[] fieldChars, final short value) { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] @@ -208,7 +202,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override - public void writeFieldIntValue(final byte[] fieldBytes, final int value) { + public void writeFieldIntValue(final byte[] fieldBytes, final char[] fieldChars, final int value) { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] @@ -225,7 +219,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override - public void writeFieldLongValue(final byte[] fieldBytes, final long value) { + public void writeFieldLongValue(final byte[] fieldBytes, final char[] fieldChars, final long value) { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] @@ -242,7 +236,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override - public void writeFieldLatin1Value(final byte[] fieldBytes, final String value) { + public void writeFieldLatin1Value(final byte[] fieldBytes, final char[] fieldChars, final String value) { byte[] bs1 = fieldBytes; byte[] bs2 = Utility.latin1ByteArray(value); int len1 = bs1.length; @@ -257,7 +251,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override - public void writeLastFieldShortValue(final byte[] fieldBytes, final short value) { + public void writeLastFieldShortValue(final byte[] fieldBytes, final char[] fieldChars, final short value) { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] @@ -275,7 +269,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override - public void writeLastFieldIntValue(final byte[] fieldBytes, final int value) { + public void writeLastFieldIntValue(final byte[] fieldBytes, final char[] fieldChars, final int value) { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] @@ -293,7 +287,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override - public void writeLastFieldLongValue(final byte[] fieldBytes, final long value) { + public void writeLastFieldLongValue(final byte[] fieldBytes, final char[] fieldChars, final long value) { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] @@ -311,7 +305,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override - public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) { + public void writeLastFieldLatin1Value(final byte[] fieldBytes, final char[] fieldChars, final String value) { if (value == null && nullable()) { writeTo(fieldBytes); writeNull(); @@ -340,7 +334,8 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } @Override // firstFieldBytes 必须带{开头 - public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) { + public void writeObjectByOnlyOneLatin1FieldValue( + final byte[] firstFieldBytes, final char[] firstFieldChars, final String value) { if (value == null && nullable()) { writeTo(BYTE_LBRACE); writeTo(firstFieldBytes); @@ -372,7 +367,12 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { @Override // firstFieldBytes 必须带{开头, lastFieldBytes必须,开头 public void writeObjectByOnlyTwoIntFieldValue( - final byte[] firstFieldBytes, final int value1, final byte[] lastFieldBytes, final int value2) { + final byte[] firstFieldBytes, + final char[] firstFieldChars, + final int value1, + final byte[] lastFieldBytes, + final char[] lastFieldChars, + final int value2) { byte[] bs1 = firstFieldBytes; byte[] bs2 = (value1 >= 0 && value1 < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value1] @@ -466,6 +466,14 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { bytes[curr++] = '\\'; bytes[curr++] = 'r'; break; + case '\f': + bytes[curr++] = '\\'; + bytes[curr++] = 'f'; + break; + case '\b': + bytes[curr++] = '\\'; + bytes[curr++] = 'b'; + break; case '\t': bytes[curr++] = '\\'; bytes[curr++] = 't'; @@ -474,9 +482,9 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { bytes[curr++] = '\\'; bytes[curr++] = '\\'; break; - case '"': + case BYTE_DQUOTE: bytes[curr++] = '\\'; - bytes[curr++] = '"'; + bytes[curr++] = BYTE_DQUOTE; break; default: if (ch < 0x80) { @@ -521,7 +529,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { if (b2 == 0 && b >= 0) { if (b == BYTE_DQUOTE) { bytes[curr++] = '\\'; - bytes[curr++] = '"'; + bytes[curr++] = BYTE_DQUOTE; } else if (b == '\\') { bytes[curr++] = '\\'; bytes[curr++] = '\\'; @@ -583,14 +591,14 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { count = curr; } - private static final char MIN_LOW_SURROGATE = '\uDC00'; - private static final char MAX_LOW_SURROGATE = '\uDFFF'; - private static final char MIN_HIGH_SURROGATE = '\uD800'; - private static final char MAX_HIGH_SURROGATE = '\uDBFF'; - private static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x010000; - private static final char MAX_LOW_SURROGATE_MORE = MAX_LOW_SURROGATE + 1; - private static final char MAX_HIGH_SURROGATE_MORE = MAX_HIGH_SURROGATE + 1; - private static final int MIN_SUPPLEMENTARY_CODE_POINT_MORE = + static final char MIN_LOW_SURROGATE = '\uDC00'; + static final char MAX_LOW_SURROGATE = '\uDFFF'; + static final char MIN_HIGH_SURROGATE = '\uD800'; + static final char MAX_HIGH_SURROGATE = '\uDBFF'; + static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x010000; + static final char MAX_LOW_SURROGATE_MORE = MAX_LOW_SURROGATE + 1; + static final char MAX_HIGH_SURROGATE_MORE = MAX_HIGH_SURROGATE + 1; + static final int MIN_SUPPLEMENTARY_CODE_POINT_MORE = (MIN_SUPPLEMENTARY_CODE_POINT - (MIN_HIGH_SURROGATE << 10) - MIN_LOW_SURROGATE); private void writeEscapeLatinString(final boolean quote, byte[] value) { @@ -602,7 +610,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { for (byte b : value) { if (b == BYTE_DQUOTE) { bytes[curr++] = '\\'; - bytes[curr++] = '"'; + bytes[curr++] = BYTE_DQUOTE; } else if (b == '\\') { bytes[curr++] = '\\'; bytes[curr++] = '\\'; diff --git a/src/main/java/org/redkale/convert/json/JsonCharsWriter.java b/src/main/java/org/redkale/convert/json/JsonCharsWriter.java new file mode 100644 index 000000000..224102275 --- /dev/null +++ b/src/main/java/org/redkale/convert/json/JsonCharsWriter.java @@ -0,0 +1,554 @@ +/* + * Copyright (c) 2016-2116 Redkale + * All rights reserved. + */ +package org.redkale.convert.json; + +import static org.redkale.convert.json.JsonWriter.BYTE_DQUOTE; +import static org.redkale.convert.json.JsonWriter.BYTE_LBRACE; +import static org.redkale.convert.json.JsonWriter.BYTE_RBRACE; +import static org.redkale.convert.json.JsonWriter.DEFAULT_SIZE; +import static org.redkale.convert.json.JsonWriter.DigitOnes; +import static org.redkale.convert.json.JsonWriter.DigitTens; +import static org.redkale.convert.json.JsonWriter.digits; +import static org.redkale.convert.json.JsonWriter.sizeTable; +import org.redkale.util.StringWrapper; +import org.redkale.util.Utility; + +/** + * writeTo系列的方法输出的字符不能含特殊字符 + * + *

详情见: https://redkale.org + * + * @author zhangjx + * @since 2.3.0 + */ +public class JsonCharsWriter extends JsonWriter { + + private static final char[] CHARS_TUREVALUE = "true".toCharArray(); + + private static final char[] CHARS_FALSEVALUE = "false".toCharArray(); + + private static final int TENTHOUSAND_MAX = 10001; + + private static final char[][] TENTHOUSAND_CHARS = new char[TENTHOUSAND_MAX][]; + private static final char[][] TENTHOUSAND_CHARS2 = new char[TENTHOUSAND_MAX][]; + + static { + for (int i = 0; i < TENTHOUSAND_CHARS.length; i++) { + TENTHOUSAND_CHARS[i] = String.valueOf(i).toCharArray(); + TENTHOUSAND_CHARS2[i] = String.valueOf(-i).toCharArray(); + } + } + + private int count; + + private char[] content; + + public JsonCharsWriter() { + this(DEFAULT_SIZE); + } + + public JsonCharsWriter(int size) { + this.content = new char[size > 1024 ? size : 1024]; + } + + // ----------------------------------------------------------------------- + /** + * 返回指定至少指定长度的缓冲区 + * + * @param len + * + * @return + */ + private char[] expand(int len) { + int ncount = count + len; + if (ncount <= content.length) { + return content; + } + char[] newdata = new char[Math.max(content.length * 3 / 2, ncount)]; + System.arraycopy(content, 0, newdata, 0, count); + this.content = newdata; + return newdata; + } + + @Override + protected boolean recycle() { + super.recycle(); + this.count = 0; + if (this.content != null && this.content.length > DEFAULT_SIZE * 100) { + this.content = new char[DEFAULT_SIZE]; + } + return true; + } + + @Override + public final boolean charsMode() { + return true; + } + + @Override + public void writeTo(final char ch) { // 只能是 0 - 127 的字符 + expand(1); + content[count++] = ch; + } + + @Override + public void writeTo(final char[] chs, final int start, final int len) { // 只能是 0 - 127 的字符 + expand(len); + System.arraycopy(chs, start, content, count, len); + count += len; + } + + @Override + public void writeTo(final byte b) { // 只能是 0 - 127 的字符 + expand(1); + content[count++] = (char) (b & 0xff); + } + + @Override + public void writeTo(final byte[] bs, final int start, final int len) { // 只能是 0 - 127 的字符 + char[] chars = expand(len); + for (int i = 0; i < len; i++) { + chars[count + i] = (char) (bs[start + i] & 0xff); + } + count += len; + } + + /** + * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger、BigDecimal转换的String + * + * @param quote 是否加双引号 + * @param value 非null且不含需要转义的字符的String值 + */ + @Override + public void writeLatin1To(final boolean quote, final String value) { + int len = value.length(); + if (quote) { + expand(len + 2); + content[count++] = BYTE_DQUOTE; + value.getChars(0, len, content, count); + count += len; + content[count++] = BYTE_DQUOTE; + } else { + expand(len); + value.getChars(0, len, content, count); + count += len; + } + } + + @Override + public void writeFieldShortValue(final byte[] fieldBytes, final char[] fieldChars, final short value) { + char[] bs1 = fieldChars; + char[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS[value] + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS2[-value] + : String.valueOf(value).toCharArray()); + int len1 = bs1.length; + int len2 = bs2.length; + char[] src = expand(len1 + len2); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + } + + @Override + public void writeFieldIntValue(final byte[] fieldBytes, final char[] fieldChars, final int value) { + char[] bs1 = fieldChars; + char[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS[value] + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS2[-value] + : String.valueOf(value).toCharArray()); + int len1 = bs1.length; + int len2 = bs2.length; + char[] src = expand(len1 + len2); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + } + + @Override + public void writeFieldLongValue(final byte[] fieldBytes, final char[] fieldChars, final long value) { + char[] bs1 = fieldChars; + char[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS[(int) value] + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS2[(int) -value] + : String.valueOf(value).toCharArray()); + int len1 = bs1.length; + int len2 = bs2.length; + char[] src = expand(len1 + len2); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + } + + @Override + public void writeFieldLatin1Value(final byte[] fieldBytes, final char[] fieldChars, final String value) { + char[] bs1 = fieldChars; + int len1 = bs1.length; + int len2 = value.length(); + char[] src = expand(len1 + len2 + 2); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + src[count++] = BYTE_DQUOTE; + value.getChars(0, len2, content, count); + count += len2; + src[count++] = BYTE_DQUOTE; + } + + @Override + public void writeLastFieldShortValue(final byte[] fieldBytes, final char[] fieldChars, final short value) { + char[] bs1 = fieldChars; + char[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS[value] + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS2[-value] + : String.valueOf(value).toCharArray()); + int len1 = bs1.length; + int len2 = bs2.length; + char[] src = expand(len1 + len2 + 1); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + src[count++] = BYTE_RBRACE; + } + + @Override + public void writeLastFieldIntValue(final byte[] fieldBytes, final char[] fieldChars, final int value) { + char[] bs1 = fieldChars; + char[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS[value] + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS2[-value] + : String.valueOf(value).toCharArray()); + int len1 = bs1.length; + int len2 = bs2.length; + char[] src = expand(len1 + len2 + 1); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + src[count++] = BYTE_RBRACE; + } + + @Override + public void writeLastFieldLongValue(final byte[] fieldBytes, final char[] fieldChars, final long value) { + char[] bs1 = fieldChars; + char[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS[(int) value] + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS2[(int) -value] + : String.valueOf(value).toCharArray()); + int len1 = bs1.length; + int len2 = bs2.length; + char[] src = expand(len1 + len2 + 1); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + src[count++] = BYTE_RBRACE; + } + + @Override + public void writeLastFieldLatin1Value(final byte[] fieldBytes, final char[] fieldChars, final String value) { + if (value == null && nullable()) { + writeTo(fieldChars); + writeNull(); + writeTo(BYTE_RBRACE); + return; + } + if (value == null || (tiny() && value.isEmpty())) { + expand(1); + content[count++] = BYTE_RBRACE; + } else { + int len1 = fieldChars.length; + int len2 = value.length(); + char[] src = expand(len1 + len2 + 3); + System.arraycopy(fieldChars, 0, src, count, len1); + count += len1; + content[count++] = BYTE_DQUOTE; + value.getChars(0, len2, content, count); + count += len2; + content[count++] = BYTE_DQUOTE; + content[count++] = BYTE_RBRACE; + } + } + + @Override // firstFieldBytes 必须带{开头 + public void writeObjectByOnlyOneLatin1FieldValue( + byte[] firstFieldBytes, char[] firstFieldChars, final String value) { + if (value == null && nullable()) { + writeTo(BYTE_LBRACE); + writeTo(firstFieldChars); + writeNull(); + writeTo(BYTE_RBRACE); + return; + } + if (value == null || (tiny() && value.isEmpty())) { + expand(2); + content[count++] = BYTE_LBRACE; + content[count++] = BYTE_RBRACE; + } else { + int len1 = firstFieldChars.length; + int len2 = value.length(); + char[] src = expand(len1 + len2 + 3); + System.arraycopy(firstFieldChars, 0, src, count, len1); + count += len1; + content[count++] = BYTE_DQUOTE; + value.getChars(0, len2, content, count); + count += len2; + content[count++] = BYTE_DQUOTE; + content[count++] = BYTE_RBRACE; + } + } + + @Override // firstFieldBytes 必须带{开头, lastFieldBytes必须,开头 + public void writeObjectByOnlyTwoIntFieldValue( + final byte[] firstFieldBytes, + final char[] firstFieldChars, + final int value1, + final byte[] lastFieldBytes, + final char[] lastFieldChars, + final int value2) { + char[] bs1 = firstFieldChars; + char[] bs2 = (value1 >= 0 && value1 < TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS[value1] + : ((value1 < 0 && value1 > -TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS2[-value1] + : String.valueOf(value1).toCharArray()); + char[] bs3 = lastFieldChars; + char[] bs4 = (value2 >= 0 && value2 < TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS[value2] + : ((value2 < 0 && value2 > -TENTHOUSAND_MAX) + ? TENTHOUSAND_CHARS2[-value2] + : String.valueOf(value2).toCharArray()); + int len1 = bs1.length; + int len2 = bs2.length; + int len3 = bs3.length; + int len4 = bs4.length; + char[] src = expand(len1 + len2 + len3 + len4 + 1); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + System.arraycopy(bs3, 0, src, count, len3); + count += len3; + System.arraycopy(bs4, 0, src, count, len4); + count += len4; + src[count++] = BYTE_RBRACE; + } + + public byte[] toBytes() { + return Utility.encodeUTF8(content, 0, count); + } + + public int count() { + return this.count; + } + + @Override + public void writeString(String value) { + writeString(true, value); + } + + @Override + public void writeString(final boolean quote, String value) { + if (value == null) { + writeNull(); + return; + } + final String str = value; + final int len = str.length(); + char[] chars = expand(len + 2); + int curr = count; + if (quote) { + chars[curr++] = BYTE_DQUOTE; + } + for (int i = 0; i < len; i++) { + char ch = str.charAt(i); + switch (ch) { + case '\n': + chars[curr++] = '\\'; + chars[curr++] = 'n'; + break; + case '\r': + chars[curr++] = '\\'; + chars[curr++] = 'r'; + break; + case '\f': + chars[curr++] = '\\'; + chars[curr++] = 'f'; + break; + case '\b': + chars[curr++] = '\\'; + chars[curr++] = 'b'; + break; + case '\t': + chars[curr++] = '\\'; + chars[curr++] = 't'; + break; + case '\\': + chars[curr++] = '\\'; + chars[curr++] = ch; + break; + case '"': + chars[curr++] = '\\'; + chars[curr++] = ch; + break; + default: + chars[curr++] = ch; + break; + } + } + if (quote) { + chars[curr++] = BYTE_DQUOTE; + } + count = curr; + } + + @Override + public String toString() { + return new String(content, 0, count); + } + + // ---------------------------------------------------------------------------------------------- + @Override + public void writeBoolean(boolean value) { + writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE); + } + + @Override + public void writeInt(int value) { + if (value >= 0 && value < TENTHOUSAND_MAX) { + writeTo(TENTHOUSAND_CHARS[value]); + return; + } + if (value < 0 && value > -TENTHOUSAND_MAX) { + writeTo(TENTHOUSAND_CHARS2[-value]); + return; + } + final char sign = value >= 0 ? 0 : '-'; + if (value < 0) value = -value; + int size; + for (int i = 0; ; i++) { + if (value <= sizeTable[i]) { + size = i + 1; + break; + } + } + if (sign != 0) size++; // 负数 + expand(size); + + int q, r; + int charPos = count + size; + + // Generate two digits per iteration + while (value >= 65536) { + q = value / 100; + // really: r = i - (q * 100); + r = value - ((q << 6) + (q << 5) + (q << 2)); + value = q; + content[--charPos] = DigitOnes[r]; + content[--charPos] = DigitTens[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i <= 65536, i); + for (; ; ) { + q = (value * 52429) >>> (16 + 3); + r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ... + content[--charPos] = digits[r]; + value = q; + if (value == 0) break; + } + if (sign != 0) content[--charPos] = sign; + count += size; + } + + @Override + public void writeLong(long value) { + if (value >= 0 && value < TENTHOUSAND_MAX) { + writeTo(TENTHOUSAND_CHARS[(int) value]); + return; + } + if (value < 0 && value > -TENTHOUSAND_MAX) { + writeTo(TENTHOUSAND_CHARS2[(int) -value]); + return; + } + final char sign = value >= 0 ? 0 : '-'; + if (value < 0) value = -value; + int size = 19; + long p = 10; + for (int i = 1; i < 19; i++) { + if (value < p) { + size = i; + break; + } + p = 10 * p; + } + if (sign != 0) size++; // 负数 + expand(size); + + long q; + int r; + int charPos = count + size; + + // Get 2 digits/iteration using longs until quotient fits into an int + while (value > Integer.MAX_VALUE) { + q = value / 100; + // really: r = i - (q * 100); + r = (int) (value - ((q << 6) + (q << 5) + (q << 2))); + value = q; + content[--charPos] = DigitOnes[r]; + content[--charPos] = DigitTens[r]; + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int) value; + while (i2 >= 65536) { + q2 = i2 / 100; + // really: r = i2 - (q * 100); + r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); + i2 = q2; + content[--charPos] = DigitOnes[r]; + content[--charPos] = DigitTens[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i2 <= 65536, i2); + for (; ; ) { + q2 = (i2 * 52429) >>> (16 + 3); + r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... + content[--charPos] = digits[r]; + i2 = q2; + if (i2 == 0) break; + } + if (sign != 0) content[--charPos] = sign; + count += size; + } + + @Override + public void writeWrapper(StringWrapper wrapper) { + if (wrapper == null || wrapper.getValue() == null) { + writeNull(); + return; + } + String value = wrapper.getValue(); + if (Utility.isLatin1(value)) { + writeTo(Utility.latin1ByteArray(value)); + return; + } + int len2 = value.length(); + char[] chars = expand(len2); + value.getChars(0, len2, chars, count); + count += len2; + } +} diff --git a/src/main/java/org/redkale/convert/json/JsonConvert.java b/src/main/java/org/redkale/convert/json/JsonConvert.java index 0c83bf4cd..8754dff18 100644 --- a/src/main/java/org/redkale/convert/json/JsonConvert.java +++ b/src/main/java/org/redkale/convert/json/JsonConvert.java @@ -31,6 +31,8 @@ public class JsonConvert extends TextConvert { private final ThreadLocal bytesWriterPool = Utility.withInitialThreadLocal(JsonBytesWriter::new); + private final ThreadLocal charsWriterPool = Utility.withInitialThreadLocal(JsonCharsWriter::new); + private final Consumer offerBytesConsumer = this::offerJsonBytesWriter; private final ThreadLocal readerPool = Utility.withInitialThreadLocal(JsonReader::new); @@ -138,6 +140,22 @@ public class JsonConvert extends TextConvert { } } + private JsonCharsWriter pollJsonCharsWriter() { + JsonCharsWriter writer = charsWriterPool.get(); + if (writer == null) { + writer = new JsonCharsWriter(); + } else { + charsWriterPool.set(null); + } + return configWrite((JsonCharsWriter) writer.withFeatures(features)); + } + + private void offerJsonCharsWriter(final JsonCharsWriter writer) { + if (writer != null) { + writer.recycle(); + charsWriterPool.set(writer); + } + } // ------------------------------ convertFrom ----------------------------------------------------------- @Override public T convertFrom(final Type type, final byte[] bytes) { @@ -305,7 +323,7 @@ public class JsonConvert extends TextConvert { if (value == null) { return "null"; } - JsonBytesWriter writer = pollJsonBytesWriter(); + JsonCharsWriter writer = pollJsonCharsWriter(); final Type t = type == null ? value.getClass() : type; Encodeable encoder = this.lastConvertEncodeable; if (encoder == null || encoder.getType() != t) { @@ -318,7 +336,7 @@ public class JsonConvert extends TextConvert { encoder.convertTo(writer, value); String result = writer.toString(); - offerJsonBytesWriter(writer); + offerJsonCharsWriter(writer); return result; } diff --git a/src/main/java/org/redkale/convert/json/JsonDynEncoder.java b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java index d98dab089..925eb98f3 100644 --- a/src/main/java/org/redkale/convert/json/JsonDynEncoder.java +++ b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java @@ -511,6 +511,12 @@ public abstract class JsonDynEncoder implements Encodeable { fv.visitEnd(); fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "FirstFieldBytes", "[B", null, null); fv.visitEnd(); + fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "FieldChars", "[C", null, null); + fv.visitEnd(); + fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "CommaFieldChars", "[C", null, null); + fv.visitEnd(); + fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "FirstFieldChars", "[C", null, null); + fv.visitEnd(); final Class fieldType = readGetSetFieldType(element); if (fieldType != String.class && !fieldType.isPrimitive()) { fv = cw.visitField(ACC_PROTECTED, fieldname + "Encoder", encodeableDesc, null, null); @@ -561,6 +567,21 @@ public abstract class JsonDynEncoder implements Encodeable { mv.visitLdcInsn("{\"" + fieldname + "\":"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false); mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "FirstFieldBytes", "[B"); + // xxxFieldChars + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn("\"" + fieldname + "\":"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "toCharArray", "()[C", false); + mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "FieldChars", "[C"); + // xxxCommaFieldChars + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn(",\"" + fieldname + "\":"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "toCharArray", "()[C", false); + mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "CommaFieldChars", "[C"); + // xxxFirstFieldChars + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn("{\"" + fieldname + "\":"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "toCharArray", "()[C", false); + mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "FirstFieldChars", "[C"); } mv.visitInsn(RETURN); mv.visitMaxs(1 + members.size(), 1 + members.size()); @@ -614,13 +635,15 @@ public abstract class JsonDynEncoder implements Encodeable { elementIndex++; AccessibleObject element = members.get(elementIndex); ConvertColumnEntry ref1 = factory.findRef(clazz, element); - final String fieldname = + final String fieldName = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); final Class fieldtype = readGetSetFieldType(element); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FirstFieldBytes", "[B"); + mv.visitFieldInsn(GETFIELD, newDynName, fieldName + "FirstFieldBytes", "[B"); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldName + "FirstFieldChars", "[C"); mv.visitVarInsn(ALOAD, 2); // String message = value.getMessage(); 加载 value if (element instanceof Field) { @@ -641,7 +664,7 @@ public abstract class JsonDynEncoder implements Encodeable { INVOKEVIRTUAL, writerName, "writeObjectByOnlyOneLatin1FieldValue", - "([BLjava/lang/String;)V", + "([B[CLjava/lang/String;)V", false); maxLocals++; } else if (onlyTwoIntFieldObjectFlag) { @@ -650,7 +673,7 @@ public abstract class JsonDynEncoder implements Encodeable { ConvertColumnEntry ref1 = factory.findRef(clazz, element1); final String fieldName1 = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element1) : ref1.name(); - final Class fieldtype1 = readGetSetFieldType(element1); + final Class fieldType1 = readGetSetFieldType(element1); elementIndex++; AccessibleObject element2 = members.get(elementIndex); @@ -663,6 +686,8 @@ public abstract class JsonDynEncoder implements Encodeable { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, newDynName, fieldName1 + "FirstFieldBytes", "[B"); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldName1 + "FirstFieldChars", "[C"); mv.visitVarInsn(ALOAD, 2); // String message = value.getMessage(); 加载 value if (element1 instanceof Field) { @@ -670,19 +695,21 @@ public abstract class JsonDynEncoder implements Encodeable { GETFIELD, valtypeName, ((Field) element1).getName(), - org.redkale.asm.Type.getDescriptor(fieldtype1)); + org.redkale.asm.Type.getDescriptor(fieldType1)); } else { mv.visitMethodInsn( INVOKEVIRTUAL, valtypeName, ((Method) element1).getName(), - "()" + org.redkale.asm.Type.getDescriptor(fieldtype1), + "()" + org.redkale.asm.Type.getDescriptor(fieldType1), false); } maxLocals++; mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, newDynName, fieldName2 + "CommaFieldBytes", "[B"); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldName2 + "CommaFieldChars", "[C"); mv.visitVarInsn(ALOAD, 2); // String message = value.getMessage(); 加载 value if (element2 instanceof Field) { @@ -701,7 +728,8 @@ public abstract class JsonDynEncoder implements Encodeable { } maxLocals++; - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeObjectByOnlyTwoIntFieldValue", "([BI[BI)V", false); + mv.visitMethodInsn( + INVOKEVIRTUAL, writerName, "writeObjectByOnlyTwoIntFieldValue", "([B[CI[B[CI)V", false); } else if (onlyShotIntLongLatin1MoreFieldObjectFlag && mustHadComma) { for (AccessibleObject element : members) { @@ -718,6 +746,12 @@ public abstract class JsonDynEncoder implements Encodeable { newDynName, fieldname + (elementIndex == 0 ? "FirstFieldBytes" : "CommaFieldBytes"), "[B"); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn( + GETFIELD, + newDynName, + fieldname + (elementIndex == 0 ? "FirstFieldChars" : "CommaFieldChars"), + "[C"); mv.visitVarInsn(ALOAD, 2); // String message = value.getMessage(); 加载 value if (element instanceof Field) { @@ -739,28 +773,28 @@ public abstract class JsonDynEncoder implements Encodeable { INVOKEVIRTUAL, writerName, elementIndex + 1 == membersSize ? "writeLastFieldShortValue" : "writeFieldShortValue", - "([BS)V", + "([B[CS)V", false); } else if (fieldtype == int.class) { mv.visitMethodInsn( INVOKEVIRTUAL, writerName, elementIndex + 1 == membersSize ? "writeLastFieldIntValue" : "writeFieldIntValue", - "([BI)V", + "([B[CI)V", false); } else if (fieldtype == long.class) { mv.visitMethodInsn( INVOKEVIRTUAL, writerName, elementIndex + 1 == membersSize ? "writeLastFieldLongValue" : "writeFieldLongValue", - "([BJ)V", + "([B[CJ)V", false); } else { mv.visitMethodInsn( INVOKEVIRTUAL, writerName, elementIndex + 1 == membersSize ? "writeLastFieldLatin1Value" : "writeFieldLatin1Value", - "([BLjava/lang/String;)V", + "([B[CLjava/lang/String;)V", false); } @@ -864,13 +898,17 @@ public abstract class JsonDynEncoder implements Encodeable { mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B"); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldChars", "[C"); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeField", "([B[C)V", false); } else { // out.writeTo(messageCommaFieldBytes); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B"); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldChars", "[C"); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeField", "([B[C)V", false); } } else { // if(comma) {} else {} 代码块 // if (comma) { start @@ -882,7 +920,9 @@ public abstract class JsonDynEncoder implements Encodeable { mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B"); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldChars", "[C"); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeField", "([B[C)V", false); Label commaelse = new Label(); mv.visitJumpInsn(GOTO, commaelse); @@ -915,7 +955,9 @@ public abstract class JsonDynEncoder implements Encodeable { mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B"); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldChars", "[C"); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeField", "([B[C)V", false); // comma = true; mv.visitInsn(ICONST_1); mv.visitVarInsn(ISTORE, 3); diff --git a/src/main/java/org/redkale/convert/json/JsonWriter.java b/src/main/java/org/redkale/convert/json/JsonWriter.java index 28772f45e..a71ce9661 100644 --- a/src/main/java/org/redkale/convert/json/JsonWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonWriter.java @@ -52,6 +52,10 @@ public abstract class JsonWriter extends Writer { return this.objExtFunc == null && this.objFieldFunc == null; } + public boolean charsMode() { + return false; + } + // ----------------------------------------------------------------------- public abstract void writeTo(final char ch); // 只能是 0 - 127 的字符 @@ -71,37 +75,52 @@ public abstract class JsonWriter extends Writer { public abstract void writeLatin1To(final boolean quote, final String value); @ClassDepends - public abstract void writeFieldShortValue(final byte[] fieldBytes, final short value); + public void writeField(final byte[] fieldBytes, final char[] fieldChars) { + if (charsMode()) { + writeTo(fieldChars, 0, fieldChars.length); + } else { + writeTo(fieldBytes, 0, fieldBytes.length); + } + } @ClassDepends - public abstract void writeFieldIntValue(final byte[] fieldBytes, final int value); + public abstract void writeFieldShortValue(final byte[] fieldBytes, final char[] fieldChars, final short value); @ClassDepends - public abstract void writeFieldLongValue(final byte[] fieldBytes, final long value); + public abstract void writeFieldIntValue(final byte[] fieldBytes, final char[] fieldChars, final int value); @ClassDepends - public abstract void writeFieldLatin1Value(final byte[] fieldBytes, final String value); + public abstract void writeFieldLongValue(final byte[] fieldBytes, final char[] fieldChars, final long value); @ClassDepends - public abstract void writeLastFieldShortValue(final byte[] fieldBytes, final short value); + public abstract void writeFieldLatin1Value(final byte[] fieldBytes, final char[] fieldChars, final String value); @ClassDepends - public abstract void writeLastFieldIntValue(final byte[] fieldBytes, final int value); + public abstract void writeLastFieldShortValue(final byte[] fieldBytes, final char[] fieldChars, final short value); @ClassDepends - public abstract void writeLastFieldLongValue(final byte[] fieldBytes, final long value); + public abstract void writeLastFieldIntValue(final byte[] fieldBytes, final char[] fieldChars, final int value); @ClassDepends - public abstract void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value); + public abstract void writeLastFieldLongValue(final byte[] fieldBytes, final char[] fieldChars, final long value); + + @ClassDepends + public abstract void writeLastFieldLatin1Value(final byte[] fieldBytes, final char[] fieldChars, String value); // firstFieldBytes 必须带{开头 @ClassDepends - public abstract void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value); + public abstract void writeObjectByOnlyOneLatin1FieldValue( + final byte[] firstFieldBytes, final char[] firstFieldChars, final String value); // firstFieldBytes 必须带{开头, lastFieldBytes必须,开头 @ClassDepends public abstract void writeObjectByOnlyTwoIntFieldValue( - final byte[] firstFieldBytes, final int value1, final byte[] lastFieldBytes, final int value2); + final byte[] firstFieldBytes, + final char[] firstFieldChars, + final int value1, + final byte[] lastFieldBytes, + final char[] lastFieldChars, + final int value2); @Override @ClassDepends @@ -127,7 +146,12 @@ public abstract class JsonWriter extends Writer { writeTo(BYTE_COMMA); } if (member != null) { - writeTo(member.getJsonFieldNameBytes()); + if (charsMode()) { + char[] chs = member.getJsonFieldNameChars(); + writeTo(chs, 0, chs.length); + } else { + writeTo(member.getJsonFieldNameBytes()); + } } else { writeLatin1To(true, fieldName); writeTo(BYTE_COLON); diff --git a/src/test/java/org/redkale/test/convert/StringWrapperTest.java b/src/test/java/org/redkale/test/convert/StringWrapperTest.java index 91915ba28..d0b8b9a49 100644 --- a/src/test/java/org/redkale/test/convert/StringWrapperTest.java +++ b/src/test/java/org/redkale/test/convert/StringWrapperTest.java @@ -35,7 +35,7 @@ public class StringWrapperTest { { String emoji = new String(new byte[] {(byte) 0xF0, (byte) 0x9F, (byte) 0x98, (byte) 0x81}, StandardCharsets.UTF_8); - String val = "{id:'带中文" + emoji + "'}"; + String val = "{id:'z带中文" + emoji + "a'}"; StringWrapper wrapper = new StringWrapper(val); if (!main) Assertions.assertEquals(val, convert.convertTo(wrapper)); if (!main)