diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..fcadb2cf9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text eol=lf diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e07c0e115..f198350a1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: timeout-minutes: 20 strategy: matrix: - os: [ ubuntu-20.04 ] + os: [ ubuntu-20.04, windows-latest, macos-latest ] java: [ 8, 11, 17, 21 ] fail-fast: false max-parallel: 16 @@ -33,6 +33,8 @@ jobs: cache: maven - name: Build with Maven and generate code coverage run: ./mvnw -V --no-transfer-progress -Pgen-javadoc -Pgen-dokka clean package + env: + JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai # https://github.com/marketplace/actions/codecov - uses: codecov/codecov-action@v3 with: diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index bf82ff01c..000000000 Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 6686a643d..d58dfb70b 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -6,7 +6,7 @@ # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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 @@ -14,5 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.0/apache-maven-3.9.0-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/README.md b/README.md index 683881f9f..cedf9af90 100644 --- a/README.md +++ b/README.md @@ -243,8 +243,8 @@ byte[] text = JSON.toJSONBytes(data); import com.alibaba.fastjson2.* val data = ... // Any -val text = text.toJSONString() // String -val bytes = text.toJSONByteArray() // ByteArray +val text = data.toJSONString() // String +val bytes = data.toJSONByteArray() // ByteArray ``` ### 2.5 使用`JSONObject`、`JSONArray` diff --git a/README_EN.md b/README_EN.md index b2892489d..a2b2b5700 100644 --- a/README_EN.md +++ b/README_EN.md @@ -234,8 +234,8 @@ byte[] text = JSON.toJSONBytes(data); import com.alibaba.fastjson2.* val data = ... // Any -val text = text.toJSONString() // String -val bytes = text.toJSONByteArray() // ByteArray +val text = data.toJSONString() // String +val bytes = data.toJSONByteArray() // ByteArray ``` ### 2.5 Use `JSONObject`, `JSONArray` diff --git a/benchmark/src/main/java/com/alibaba/fastjson2/benchmark/DateWrite.java b/benchmark/src/main/java/com/alibaba/fastjson2/benchmark/DateWrite.java index 93361b331..ceb3870ed 100644 --- a/benchmark/src/main/java/com/alibaba/fastjson2/benchmark/DateWrite.java +++ b/benchmark/src/main/java/com/alibaba/fastjson2/benchmark/DateWrite.java @@ -78,7 +78,7 @@ public void formatYYYYMMDDHHMMSS19(Blackhole bh) throws Throwable { static String formatYYYYMMDDHHMMSS19(ZoneId zoneId, Date date) throws Throwable { long millis = date.getTime(); - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(millis, 1000L); int offsetTotalSeconds; @@ -90,8 +90,8 @@ static String formatYYYYMMDDHHMMSS19(ZoneId zoneId, Date date) throws Throwable } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; diff --git a/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/annotation/MapTest.java b/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/annotation/MapTest.java index 1df41aa1c..7447a3cf7 100644 --- a/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/annotation/MapTest.java +++ b/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/annotation/MapTest.java @@ -1,11 +1,12 @@ package com.alibaba.fastjson2.internal.processor.annotation; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.annotation.JSONCompiled; import org.junit.jupiter.api.Test; -import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -13,7 +14,7 @@ public class MapTest { @Test public void test() { Bean bean = new Bean(); - bean.values = new HashMap<>(); + bean.values = new TreeMap<>(); bean.values.put("a", "101"); bean.values.put("b", "201"); @@ -21,7 +22,9 @@ public void test() { Bean bean1 = JSON.parseObject(str, Bean.class); assertEquals(bean.values.size(), bean1.values.size()); String str1 = JSON.toJSONString(bean1); - assertEquals(str, str1); + JSONObject json1 = JSON.parseObject(str); + JSONObject json2 = JSON.parseObject(str1); + assertEquals(json1, json2); } @JSONCompiled @@ -32,7 +35,7 @@ public static class Bean{ @Test public void test1() { Bean1 bean = new Bean1(); - bean.values = new HashMap<>(); + bean.values = new TreeMap<>(); bean.values.put("a", "101"); bean.values.put("b", "201"); @@ -40,7 +43,9 @@ public void test1() { Bean1 bean1 = JSON.parseObject(str, Bean1.class); assertEquals(bean.values.size(), bean1.values.size()); String str1 = JSON.toJSONString(bean1); - assertEquals(str, str1); + JSONObject json1 = JSON.parseObject(str); + JSONObject json2 = JSON.parseObject(str1); + assertEquals(json1, json2); } @JSONCompiled @@ -51,7 +56,7 @@ public static class Bean1{ @Test public void test2() { Bean2 bean = new Bean2(); - bean.values = new HashMap<>(); + bean.values = new TreeMap<>(); bean.values.put("a", "101"); bean.values.put("b", "201"); @@ -59,7 +64,9 @@ public void test2() { Bean2 bean1 = JSON.parseObject(str, Bean2.class); assertEquals(bean.values.size(), bean1.values.size()); String str1 = JSON.toJSONString(bean1); - assertEquals(str, str1); + JSONObject json1 = JSON.parseObject(str); + JSONObject json2 = JSON.parseObject(str1); + assertEquals(json1, json2); } @JSONCompiled diff --git a/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/primitives/DateTypeTest.java b/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/primitives/DateTypeTest.java index ae961135a..742de379b 100644 --- a/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/primitives/DateTypeTest.java +++ b/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/primitives/DateTypeTest.java @@ -4,11 +4,14 @@ import com.alibaba.fastjson2.annotation.JSONCompiled; import org.junit.jupiter.api.Test; +import java.util.TimeZone; + import static org.junit.jupiter.api.Assertions.assertEquals; public class DateTypeTest { @Test public void test() { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); Bean bean = new Bean(); bean.v01 = new java.util.Date(); bean.v02 = java.util.Calendar.getInstance(); diff --git a/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/primitives/UtilTypeTest.java b/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/primitives/UtilTypeTest.java index 7db2f45ce..79da47dba 100644 --- a/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/primitives/UtilTypeTest.java +++ b/codegen-test/src/test/java/com/alibaba/fastjson2/internal/processor/primitives/UtilTypeTest.java @@ -2,6 +2,7 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.annotation.JSONCompiled; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.*; @@ -9,6 +10,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class UtilTypeTest { + @BeforeAll + static void setUp() { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } + @Test public void test() { Bean bean = new Bean(); diff --git a/core/src/main/java/com/alibaba/fastjson2/JSON.java b/core/src/main/java/com/alibaba/fastjson2/JSON.java index 4b0a2269e..838ec728b 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSON.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSON.java @@ -3859,7 +3859,9 @@ static Object toJSON(Object object, JSONWriter.Feature... features) { JSONFactory.createWriteContext() : JSONFactory.createWriteContext(features); Class valueClass = object.getClass(); ObjectWriter objectWriter = writeContext.getObjectWriter(valueClass, valueClass); - if (objectWriter instanceof ObjectWriterAdapter && !writeContext.isEnabled(JSONWriter.Feature.ReferenceDetection)) { + if (objectWriter instanceof ObjectWriterAdapter + && !writeContext.isEnabled(JSONWriter.Feature.ReferenceDetection) + && (objectWriter.getFeatures() & JSONWriter.Feature.WriteClassName.mask) == 0) { ObjectWriterAdapter objectWriterAdapter = (ObjectWriterAdapter) objectWriter; return objectWriterAdapter.toJSONObject(object, writeContext.features); } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONArray.java b/core/src/main/java/com/alibaba/fastjson2/JSONArray.java index 9da34e633..e08147ca4 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONArray.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONArray.java @@ -984,7 +984,6 @@ public byte[] toJSONBBytes(JSONWriter.Feature... features) { * @param type specify the {@link Type} to be converted * @since 2.0.4 */ - @SuppressWarnings("unchecked") public T to(Type type) { return to(type, 0L); } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONBDump.java b/core/src/main/java/com/alibaba/fastjson2/JSONBDump.java index 916d01aa8..246e291e3 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONBDump.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONBDump.java @@ -15,7 +15,6 @@ import static com.alibaba.fastjson2.JSONB.Constants.*; import static com.alibaba.fastjson2.JSONB.typeName; import static com.alibaba.fastjson2.util.JDKUtils.*; -import static com.alibaba.fastjson2.util.JDKUtils.BIG_ENDIAN; final class JSONBDump { static Charset GB18030; diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONObject.java b/core/src/main/java/com/alibaba/fastjson2/JSONObject.java index 76d66f148..c0740475f 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONObject.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONObject.java @@ -23,8 +23,6 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import static com.alibaba.fastjson2.JSONWriter.Feature.*; import static com.alibaba.fastjson2.util.BeanUtils.getAnnotations; @@ -542,31 +540,7 @@ public Long getLong(String key) { * @throws JSONException Unsupported type conversion to long value */ public long getLongValue(String key) { - Object value = super.get(key); - - if (value == null) { - return 0; - } - - if (value instanceof Number) { - return ((Number) value).longValue(); - } - - if (value instanceof String) { - String str = (String) value; - - if (str.isEmpty() || "null".equalsIgnoreCase(str)) { - return 0; - } - - if (str.indexOf('.') != -1) { - return (long) Double.parseDouble(str); - } - - return Long.parseLong(str); - } - - throw new JSONException("Can not cast '" + value.getClass() + "' to long value"); + return getLongValue(key, 0); } /** @@ -659,31 +633,7 @@ public Integer getInteger(String key) { * @throws JSONException Unsupported type conversion to int value */ public int getIntValue(String key) { - Object value = super.get(key); - - if (value == null) { - return 0; - } - - if (value instanceof Number) { - return ((Number) value).intValue(); - } - - if (value instanceof String) { - String str = (String) value; - - if (str.isEmpty() || "null".equalsIgnoreCase(str)) { - return 0; - } - - if (str.indexOf('.') != -1) { - return (int) Double.parseDouble(str); - } - - return Integer.parseInt(str); - } - - throw new JSONException("Can not cast '" + value.getClass() + "' to int value"); + return getIntValue(key, 0); } /** @@ -756,7 +706,7 @@ public Short getShort(String key) { return Short.parseShort(str); } - throw new JSONException("Can not cast '" + value.getClass() + "' to Short"); + throw new JSONException("Can not cast '" + value.getClass() + "' to short"); } /** @@ -768,27 +718,8 @@ public Short getShort(String key) { * @throws JSONException Unsupported type conversion to short value */ public short getShortValue(String key) { - Object value = super.get(key); - - if (value == null) { - return 0; - } - - if (value instanceof Number) { - return ((Number) value).shortValue(); - } - - if (value instanceof String) { - String str = (String) value; - - if (str.isEmpty() || "null".equalsIgnoreCase(str)) { - return 0; - } - - return Short.parseShort(str); - } - - throw new JSONException("Can not cast '" + value.getClass() + "' to short value"); + Short value = getShort(key); + return value == null ? 0 : value; } /** @@ -820,7 +751,7 @@ public Byte getByte(String key) { return Byte.parseByte(str); } - throw new JSONException("Can not cast '" + value.getClass() + "' to Byte"); + throw new JSONException("Can not cast '" + value.getClass() + "' to byte"); } /** @@ -832,27 +763,8 @@ public Byte getByte(String key) { * @throws JSONException Unsupported type conversion to byte value */ public byte getByteValue(String key) { - Object value = super.get(key); - - if (value == null) { - return 0; - } - - if (value instanceof Number) { - return ((Number) value).byteValue(); - } - - if (value instanceof String) { - String str = (String) value; - - if (str.isEmpty() || "null".equalsIgnoreCase(str)) { - return 0; - } - - return Byte.parseByte(str); - } - - throw new JSONException("Can not cast '" + value.getClass() + "' to byte value"); + Byte value = getByte(key); + return value == null ? 0 : value; } public byte[] getBytes(String key) { @@ -903,7 +815,7 @@ public Boolean getBoolean(String key) { return "true".equalsIgnoreCase(str) || "1".equals(str); } - throw new JSONException("Can not cast '" + value.getClass() + "' to Boolean"); + throw new JSONException("Can not cast '" + value.getClass() + "' to boolean"); } /** @@ -914,26 +826,8 @@ public Boolean getBoolean(String key) { * @throws JSONException Unsupported type conversion to boolean value */ public boolean getBooleanValue(String key) { - Object value = super.get(key); - - if (value == null) { - return false; - } - - if (value instanceof Boolean) { - return (Boolean) value; - } - - if (value instanceof Number) { - return ((Number) value).intValue() == 1; - } - - if (value instanceof String) { - String str = (String) value; - return "true".equalsIgnoreCase(str) || "1".equals(str); - } - - throw new JSONException("Can not cast '" + value.getClass() + "' to boolean value"); + Boolean value = getBoolean(key); + return value != null && value; } /** @@ -945,26 +839,8 @@ public boolean getBooleanValue(String key) { * @throws JSONException Unsupported type conversion to boolean value */ public boolean getBooleanValue(String key, boolean defaultValue) { - Object value = super.get(key); - - if (value == null) { - return defaultValue; - } - - if (value instanceof Boolean) { - return (Boolean) value; - } - - if (value instanceof Number) { - return ((Number) value).intValue() == 1; - } - - if (value instanceof String) { - String str = (String) value; - return "true".equalsIgnoreCase(str) || "1".equals(str); - } - - throw new JSONException("Can not cast '" + value.getClass() + "' to boolean value"); + Boolean value = getBoolean(key); + return value == null ? defaultValue : value; } /** @@ -2075,29 +1951,35 @@ public static JSONObject of( * @param kvArray key-value * @since 2.0.53 */ - private static JSONObject of(JSONObject object, Object... kvArray) { - if (kvArray == null || kvArray.length <= 0) { + private static JSONObject of(JSONObject jsonObject, Object... kvArray) { + if (kvArray == null || kvArray.length == 0) { throw new JSONException("The kvArray cannot be empty"); } - int kvArrayLength = kvArray.length; + final int kvArrayLength = kvArray.length; if ((kvArrayLength & 1) == 1) { throw new JSONException("The length of kvArray cannot be odd"); } - List keyList = IntStream.range(0, kvArrayLength).filter(i -> i % 2 == 0).mapToObj(i -> kvArray[i]).collect(Collectors.toList()); - keyList.forEach(key -> { - if (key == null || !(key instanceof String)) { + boolean valueMaybeNull = false; + for (int i = 0; i < kvArrayLength; i++) { + Object keyObj = kvArray[i++]; + if (!(keyObj instanceof String)) { throw new JSONException("The value corresponding to the even bit index of kvArray is key, which cannot be null and must be of type string"); } - }); - List distinctKeyList = keyList.stream().distinct().collect(Collectors.toList()); - if (keyList.size() != distinctKeyList.size()) { - throw new JSONException("The value corresponding to the even bit index of kvArray is key and cannot be duplicated"); - } - List valueList = IntStream.range(0, kvArrayLength).filter(i -> i % 2 != 0).mapToObj(i -> kvArray[i]).collect(Collectors.toList()); - for (int i = 0; i < keyList.size(); i++) { - object.put(keyList.get(i).toString(), valueList.get(i)); + String key = (String) keyObj; + if (valueMaybeNull) { + if (jsonObject.containsKey(key)) { + throw new JSONException("The value corresponding to the even bit index of kvArray is key and cannot be duplicated"); + } + jsonObject.put(key, kvArray[i]); + } else { + Object old = jsonObject.put(key, kvArray[i]); + if (old != null) { + throw new JSONException("The value corresponding to the even bit index of kvArray is key and cannot be duplicated"); + } + valueMaybeNull = kvArray[i] == null; + } } - return object; + return jsonObject; } /** diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONPathFunction.java b/core/src/main/java/com/alibaba/fastjson2/JSONPathFunction.java index 091cb3f8d..54fe2e0ab 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONPathFunction.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONPathFunction.java @@ -603,10 +603,7 @@ public Object apply(Object o) { } if (o.getClass().isArray()) { - int len = Array.getLength(o); - for (int i = 0; i < len; i++) { - return Array.get(o, i); - } + return Array.get(o, index); } return null; diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONPathSegment.java b/core/src/main/java/com/alibaba/fastjson2/JSONPathSegment.java index 06ef7fe0d..bcfcd47a6 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONPathSegment.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONPathSegment.java @@ -1285,10 +1285,7 @@ public void accept(JSONReader jsonReader, JSONPath.Context context) { context.value = values; } context.eval = true; - return; } - - throw new JSONException("TODO"); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONPathSegmentName.java b/core/src/main/java/com/alibaba/fastjson2/JSONPathSegmentName.java index 49de0e29a..a0ad4eb78 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONPathSegmentName.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONPathSegmentName.java @@ -378,7 +378,7 @@ public void set(JSONPath.Context context, Object value) { ObjectReader objectReader = provider.getObjectReader(item.getClass()); FieldReader fieldReader = objectReader.getFieldReader(nameHashCode); if (fieldReader != null) { - fieldReader.accept(item, null); + fieldReader.accept(item, value); } } return; diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java index dd570146c..35a1ec348 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java @@ -656,7 +656,7 @@ public final String readFieldNameUnquote() { } readFieldNameHashCodeUnquote(); String name = getFieldName(); - if (name == null || name.equals("")) { + if (name == null || name.isEmpty()) { throw new JSONException(info("illegal input")); } return name; @@ -899,6 +899,9 @@ public final long getInt64Value() { } return number.longValue(); case JSON_TYPE_DEC: + case JSON_TYPE_INT64: + case JSON_TYPE_FLOAT: + case JSON_TYPE_DOUBLE: return getNumber().longValue(); case JSON_TYPE_BOOL: return boolValue ? 1 : 0; @@ -916,11 +919,6 @@ public final long getInt64Value() { case JSON_TYPE_ARRAY: { return toInt((List) complex); } - case JSON_TYPE_INT64: - case JSON_TYPE_FLOAT: - case JSON_TYPE_DOUBLE: - return getNumber() - .longValue(); case JSON_TYPE_BIG_DEC: try { return getBigDecimal() @@ -1829,7 +1827,7 @@ public Character readCharacter() { wasNull = true; return '\0'; } - return Character.valueOf(str.charAt(0)); + return str.charAt(0); } public abstract void readNull(); @@ -2823,7 +2821,7 @@ public final Number getNumber() { } if ((context.features & Feature.UseLongForInts.mask) != 0) { - return Long.valueOf(intValue); + return (long) intValue; } if (valueType == JSON_TYPE_INT64) { @@ -4661,7 +4659,7 @@ static JSONException numberError(int offset, int ch) { } JSONException numberError() { - return new JSONException("illegal number, offset " + offset + ", char " + (char) ch); + return new JSONException("illegal number, offset " + offset + ", char " + ch); } public final String info() { diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java index 9f49b67b2..99019dad2 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java @@ -1015,7 +1015,7 @@ public final long readFieldNameHashCodeUnquote() { for (int i = 0; ; ++i) { if (ch == '\\') { nameEscape = true; - ch = (char) chars[offset++]; + ch = chars[offset++]; switch (ch) { case 'u': { ch = char4(chars[offset], chars[offset + 1], chars[offset + 2], chars[offset + 3]); @@ -3279,7 +3279,7 @@ private void skipString() { ch = chars[offset]; } offset++; - } else if (ch != EOI && ch != '}' && ch != ']' && ch != EOI) { + } else if (ch != '}' && ch != ']' && ch != EOI) { throw error(offset, ch); } @@ -4165,7 +4165,7 @@ public final Date readNullOrNewDate() { Date date = null; final char[] chars = this.chars; int offset = this.offset; - char ch = this.ch; + char ch; if (offset + 2 < end && chars[offset] == 'u' && chars[offset + 1] == 'l' @@ -4317,7 +4317,7 @@ public final BigDecimal readBigDecimal() { ch = chars[offset++]; if (ch == quote) { - this.ch = offset == end ? EOI : (char) chars[offset++]; + this.ch = offset == end ? EOI : chars[offset++]; this.offset = offset; nextIfComma(); return null; diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java index 33f8a9393..8dc73cb64 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java @@ -734,11 +734,10 @@ public long readFieldNameHashCodeUnquote() { int c1 = (ch & 0x3ff) + '\uDC00'; // Character.lowSurrogate(uc); hashCode ^= c1; - hashCode *= Fnv.MAGIC_PRIME; } else { hashCode ^= ch; - hashCode *= Fnv.MAGIC_PRIME; } + hashCode *= Fnv.MAGIC_PRIME; ch = offset == end ? EOI : bytes[offset++]; } @@ -5836,7 +5835,7 @@ public final boolean isNull() { @Override public final Date readNullOrNewDate() { final byte[] bytes = this.bytes; - int ch = this.ch; + int ch; int offset = this.offset; Date date = null; @@ -5938,7 +5937,7 @@ public final boolean nextIfNull() { public final void readNull() { final byte[] bytes = this.bytes; int offset = this.offset; - int ch = this.ch; + int ch; if (bytes[offset] == 'u' && bytes[offset + 1] == 'l' && bytes[offset + 2] == 'l') { diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java index 777033848..209a20788 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java @@ -1,5 +1,6 @@ package com.alibaba.fastjson2; +import com.alibaba.fastjson2.codec.FieldInfo; import com.alibaba.fastjson2.filter.*; import com.alibaba.fastjson2.util.IOUtils; import com.alibaba.fastjson2.util.TypeUtils; @@ -29,6 +30,7 @@ public abstract class JSONWriter implements Closeable { static final long WRITE_ARRAY_NULL_MASK = NullAsDefaultValue.mask | WriteNullListAsEmpty.mask; static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + static final byte PRETTY_NON = 0, PRETTY_TAB = 1, PRETTY_2_SPACE = 2, PRETTY_4_SPACE = 4; public final Context context; public final boolean utf8; @@ -48,8 +50,7 @@ public abstract class JSONWriter protected IdentityHashMap refs; protected Path path; protected String lastReference; - protected boolean pretty; - protected int indent; + protected byte pretty; protected Object attachment; protected JSONWriter( @@ -70,7 +71,15 @@ protected JSONWriter( // 64M or 1G maxArraySize = (context.features & LargeObject.mask) != 0 ? 1073741824 : 67108864; - pretty = (context.features & PrettyFormat.mask) != 0; + if ((context.features & PrettyFormatWith4Space.mask) != 0) { + pretty = PRETTY_4_SPACE; + } else if ((context.features & PrettyFormatWith2Space.mask) != 0) { + pretty = PRETTY_2_SPACE; + } else if ((context.features & PrettyFormat.mask) != 0) { + pretty = PRETTY_TAB; + } else { + pretty = PRETTY_NON; + } } public final Charset getCharset() { @@ -245,7 +254,8 @@ public final boolean isWriteNulls() { } public final boolean isRefDetect() { - return (context.features & ReferenceDetection.mask) != 0; + return (context.features & ReferenceDetection.mask) != 0 + && (context.features & FieldInfo.DISABLE_REFERENCE_DETECT) == 0; } public final boolean isUseSingleQuotes() { @@ -254,6 +264,7 @@ public final boolean isUseSingleQuotes() { public final boolean isRefDetect(Object object) { return (context.features & ReferenceDetection.mask) != 0 + && (context.features & FieldInfo.DISABLE_REFERENCE_DETECT) == 0 && object != null && !ObjectWriterProvider.isNotReferenceDetect(object.getClass()); } @@ -679,8 +690,8 @@ public static JSONWriter ofPretty() { } public static JSONWriter ofPretty(JSONWriter writer) { - if (!writer.pretty) { - writer.pretty = true; + if (writer.pretty == PRETTY_NON) { + writer.pretty = PRETTY_TAB; writer.context.features |= PrettyFormat.mask; } return writer; @@ -1609,6 +1620,11 @@ public void write(Map map) { return; } + if (map.isEmpty()) { + writeRaw('{', '}'); + return; + } + final long NONE_DIRECT_FEATURES = ReferenceDetection.mask | PrettyFormat.mask | NotWriteEmptyArray.mask @@ -1638,7 +1654,97 @@ public void write(Map map) { write0('}'); } - public abstract void write(JSONObject map); + public void write(JSONObject map) { + if (map == null) { + this.writeNull(); + return; + } + + if (map.isEmpty()) { + writeRaw('{', '}'); + return; + } + + final long NONE_DIRECT_FEATURES = ReferenceDetection.mask + | NotWriteEmptyArray.mask + | NotWriteDefaultValue.mask; + + if ((context.features & NONE_DIRECT_FEATURES) != 0) { + ObjectWriter objectWriter = context.getObjectWriter(map.getClass()); + objectWriter.write(this, map, null, null, 0); + return; + } + + startObject(); + + boolean first = true; + for (Map.Entry entry : map.entrySet()) { + Object value = entry.getValue(); + if (value == null && (context.features & WriteMapNullValue.mask) == 0) { + continue; + } + + if (!first) { + writeComma(); + } + + first = false; + Object key = entry.getKey(); + if (key instanceof String) { + writeString((String) key); + } else { + writeAny(key); + } + + writeColon(); + + if (value == null) { + writeNull(); + continue; + } + + Class valueClass = value.getClass(); + if (valueClass == String.class) { + writeString((String) value); + continue; + } + + if (valueClass == Integer.class) { + writeInt32((Integer) value); + continue; + } + + if (valueClass == Long.class) { + writeInt64((Long) value); + continue; + } + + if (valueClass == Boolean.class) { + writeBool((Boolean) value); + continue; + } + + if (valueClass == BigDecimal.class) { + writeDecimal((BigDecimal) value, 0, null); + continue; + } + + if (valueClass == JSONArray.class) { + write((JSONArray) value); + continue; + } + + if (valueClass == JSONObject.class) { + write((JSONObject) value); + continue; + } + + ObjectWriter objectWriter = context.getObjectWriter(valueClass, valueClass); + objectWriter.write(this, value, null, null, 0); + } + + endObject(); + } public void writeAny(Object value) { if (value == null) { @@ -2209,7 +2315,19 @@ public enum Feature { * SortedMap and derived classes do not need to do this. * @since 2.0.48 */ - SortMapEntriesByKeys(1L << 41); + SortMapEntriesByKeys(1L << 41), + + /** + * JSON formatting support using 4 spaces for indentation + * @since 2.0.54 + */ + PrettyFormatWith2Space(1L << 42), + + /** + * JSON formatting support using 4 spaces for indentation + * @since 2.0.54 + */ + PrettyFormatWith4Space(1L << 43); public final long mask; @@ -2579,23 +2697,23 @@ protected static IllegalArgumentException illegalYear(int year) { * @deprecated */ public final void incrementIndent() { - indent++; + level++; } /** * @deprecated */ public final void decrementIdent() { - indent--; + level--; } /** * @deprecated */ public void println() { - writeChar('\n'); - for (int i = 0; i < indent; ++i) { - writeChar('\t'); + writeRaw('\n'); + for (int i = 0; i < level; ++i) { + writeRaw('\t'); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java index 9a94332f5..9446c7952 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java @@ -1621,14 +1621,11 @@ public void writeInt8(byte[] values) { } final byte[] bytes = this.bytes; - for (int i = 0; i < values.length; i++) { - int val = values[i]; - if (val >= BC_INT32_NUM_MIN && val <= BC_INT32_NUM_MAX) { - bytes[off++] = (byte) val; - } else { + for (int val : values) { + if (val < BC_INT32_NUM_MIN || val > BC_INT32_NUM_MAX) { bytes[off++] = (byte) (BC_INT32_BYTE_ZERO + (val >> 8)); - bytes[off++] = (byte) (val); } + bytes[off++] = (byte) val; } this.off = off; } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java index b850c217e..c4db8c4bc 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java @@ -126,7 +126,7 @@ public final void startObject() { startObject = true; int off = this.off; - int minCapacity = off + (pretty ? 3 + indent : 1); + int minCapacity = off + 3 + pretty * level; if (minCapacity >= chars.length) { ensureCapacity(minCapacity); } @@ -134,12 +134,8 @@ public final void startObject() { final char[] chars = this.chars; chars[off++] = (byte) '{'; - if (pretty) { - indent++; - chars[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - chars[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(chars, off); } this.off = off; } @@ -148,18 +144,14 @@ public final void startObject() { public final void endObject() { level--; int off = this.off; - int minCapacity = off + (pretty ? 2 + indent : 1); + int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1); if (minCapacity >= chars.length) { ensureCapacity(minCapacity); } final char[] chars = this.chars; - if (pretty) { - indent--; - chars[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - chars[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(chars, off); } chars[off] = (byte) '}'; @@ -171,18 +163,15 @@ public final void endObject() { public final void writeComma() { startObject = false; int off = this.off; - int minCapacity = off + (pretty ? 2 + indent : 1); + int minCapacity = off + 2 + pretty * level; if (minCapacity >= chars.length) { ensureCapacity(minCapacity); } final char[] chars = this.chars; chars[off++] = (byte) ','; - if (pretty) { - chars[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - chars[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(chars, off); } this.off = off; } @@ -195,19 +184,15 @@ public final void startArray() { level++; int off = this.off; - int minCapacity = off + (pretty ? 3 + indent : 1); + int minCapacity = off + 3 + pretty * level; if (minCapacity >= chars.length) { ensureCapacity(minCapacity); } final char[] chars = this.chars; chars[off++] = (byte) '['; - if (pretty) { - indent++; - chars[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - chars[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(chars, off); } this.off = off; } @@ -216,18 +201,14 @@ public final void startArray() { public final void endArray() { level--; int off = this.off; - int minCapacity = off + (pretty ? 2 + indent : 1); + int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1); if (minCapacity >= chars.length) { ensureCapacity(minCapacity); } final char[] chars = this.chars; - if (pretty) { - indent--; - chars[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - chars[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(chars, off); } chars[off] = (byte) ']'; this.off = off + 1; @@ -235,6 +216,10 @@ public final void endArray() { } public final void writeString(List list) { + if (pretty != PRETTY_NON) { + super.writeString(list); + return; + } // startArray(); if (off == chars.length) { ensureCapacity(off + 1); @@ -1542,7 +1527,7 @@ public final void writeRaw(char c0, char c1) { @Override public final void writeNameRaw(char[] name) { int off = this.off; - int minCapacity = off + name.length + 2 + indent; + int minCapacity = off + name.length + 2 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1552,8 +1537,8 @@ public final void writeNameRaw(char[] name) { } else { final char[] chars = this.chars; chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } System.arraycopy(name, 0, chars, off, name.length); @@ -1563,7 +1548,7 @@ public final void writeNameRaw(char[] name) { @Override public final void writeName2Raw(long name) { int off = this.off; - int minCapacity = off + 10 + indent; + int minCapacity = off + 10 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1573,8 +1558,8 @@ public final void writeName2Raw(long name) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1585,7 +1570,7 @@ public final void writeName2Raw(long name) { @Override public final void writeName3Raw(long name) { int off = this.off; - int minCapacity = off + 10 + indent; + int minCapacity = off + 10 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1595,8 +1580,8 @@ public final void writeName3Raw(long name) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1607,7 +1592,7 @@ public final void writeName3Raw(long name) { @Override public final void writeName4Raw(long name) { int off = this.off; - int minCapacity = off + 10 + indent; + int minCapacity = off + 10 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1617,8 +1602,8 @@ public final void writeName4Raw(long name) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1629,7 +1614,7 @@ public final void writeName4Raw(long name) { @Override public final void writeName5Raw(long name) { int off = this.off; - int minCapacity = off + 10 + indent; + int minCapacity = off + 10 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1639,8 +1624,8 @@ public final void writeName5Raw(long name) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1651,7 +1636,7 @@ public final void writeName5Raw(long name) { @Override public final void writeName6Raw(long name) { int off = this.off; - int minCapacity = off + 11 + indent; + int minCapacity = off + 11 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1661,8 +1646,8 @@ public final void writeName6Raw(long name) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1674,7 +1659,7 @@ public final void writeName6Raw(long name) { @Override public final void writeName7Raw(long name) { int off = this.off; - int minCapacity = off + 12 + indent; + int minCapacity = off + 12 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1684,8 +1669,8 @@ public final void writeName7Raw(long name) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1698,7 +1683,7 @@ public final void writeName7Raw(long name) { @Override public final void writeName8Raw(long name) { int off = this.off; - int minCapacity = off + 13 + indent; + int minCapacity = off + 13 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1708,8 +1693,8 @@ public final void writeName8Raw(long name) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1723,7 +1708,7 @@ public final void writeName8Raw(long name) { @Override public final void writeName9Raw(long name0, int name1) { int off = this.off; - int minCapacity = off + 14 + indent; + int minCapacity = off + 14 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1733,8 +1718,8 @@ public final void writeName9Raw(long name0, int name1) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1745,7 +1730,7 @@ public final void writeName9Raw(long name0, int name1) { @Override public final void writeName10Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 18 + indent; + int minCapacity = off + 18 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1755,8 +1740,8 @@ public final void writeName10Raw(long name0, long name1) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1767,7 +1752,7 @@ public final void writeName10Raw(long name0, long name1) { @Override public final void writeName11Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 18 + indent; + int minCapacity = off + 18 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1777,8 +1762,8 @@ public final void writeName11Raw(long name0, long name1) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1789,7 +1774,7 @@ public final void writeName11Raw(long name0, long name1) { @Override public final void writeName12Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 18 + indent; + int minCapacity = off + 18 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1799,8 +1784,8 @@ public final void writeName12Raw(long name0, long name1) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1811,7 +1796,7 @@ public final void writeName12Raw(long name0, long name1) { @Override public final void writeName13Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 18 + indent; + int minCapacity = off + 18 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1821,8 +1806,8 @@ public final void writeName13Raw(long name0, long name1) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1833,7 +1818,7 @@ public final void writeName13Raw(long name0, long name1) { @Override public final void writeName14Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 19 + indent; + int minCapacity = off + 19 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1843,8 +1828,8 @@ public final void writeName14Raw(long name0, long name1) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1856,7 +1841,7 @@ public final void writeName14Raw(long name0, long name1) { @Override public final void writeName15Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 20 + indent; + int minCapacity = off + 20 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1866,8 +1851,8 @@ public final void writeName15Raw(long name0, long name1) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1880,7 +1865,7 @@ public final void writeName15Raw(long name0, long name1) { @Override public final void writeName16Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 21 + indent; + int minCapacity = off + 21 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -1890,8 +1875,8 @@ public final void writeName16Raw(long name0, long name1) { startObject = false; } else { chars[off++] = ','; - if (pretty) { - off = indent(chars, off, indent); + if (pretty != PRETTY_NON) { + off = indent(chars, off); } } @@ -1962,18 +1947,16 @@ private static void putLong(char[] chars, int off, long name, long name1) { | ((name1 & 0xFF00_0000_0000_0000L) >> 8)); } - private static int indent(char[] chars, int off, int indent) { - chars[off++] = '\n'; - int end = off + indent; - while (off < end) { - chars[off++] = '\t'; - } - return off; + private int indent(char[] chars, int off) { + chars[off] = '\n'; + int toIndex = off + 1 + pretty * level; + Arrays.fill(chars, off + 1, toIndex, pretty == PRETTY_TAB ? '\t' : ' '); + return toIndex; } @Override public final void writeNameRaw(char[] chars, int off, int len) { - int minCapacity = this.off + len + 2 + indent; + int minCapacity = this.off + len + 2 + pretty * level; if (minCapacity >= this.chars.length) { ensureCapacity(minCapacity); } @@ -2946,6 +2929,11 @@ public final void writeRaw(byte[] bytes) { @Override public final void write(JSONObject map) { + if (pretty != PRETTY_NON) { + super.write(map); + return; + } + if (map == null) { this.writeNull(); return; @@ -3039,10 +3027,7 @@ public final void write(JSONObject map) { objectWriter.write(this, value, null, null, 0); } - if (off == chars.length) { - ensureCapacity(off + 1); - } - chars[off++] = '}'; + endObject(); } @Override diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java index 1797b4586..4e9bc3449 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java @@ -222,7 +222,7 @@ public final void startObject() { startObject = true; int off = this.off; - int minCapacity = off + (pretty ? 3 + indent : 1); + int minCapacity = off + 3 + pretty * level; if (minCapacity >= bytes.length) { ensureCapacity(minCapacity); } @@ -230,12 +230,8 @@ public final void startObject() { final byte[] bytes = this.bytes; bytes[off++] = (byte) '{'; - if (pretty) { - indent++; - bytes[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - bytes[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } this.off = off; } @@ -244,18 +240,14 @@ public final void startObject() { public final void endObject() { level--; int off = this.off; - int minCapacity = off + (pretty ? 2 + indent : 1); + int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1); if (minCapacity >= bytes.length) { ensureCapacity(minCapacity); } final byte[] bytes = this.bytes; - if (pretty) { - indent--; - bytes[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - bytes[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } bytes[off] = (byte) '}'; @@ -267,18 +259,15 @@ public final void endObject() { public final void writeComma() { startObject = false; int off = this.off; - int minCapacity = off + (pretty ? 2 + indent : 1); + int minCapacity = off + 2 + pretty * level; if (minCapacity >= bytes.length) { ensureCapacity(minCapacity); } final byte[] bytes = this.bytes; bytes[off++] = ','; - if (pretty) { - bytes[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - bytes[off++] = '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } this.off = off; } @@ -291,19 +280,15 @@ public final void startArray() { level++; int off = this.off; - int minCapacity = off + (pretty ? 3 + indent : 1); + int minCapacity = off + 3 + pretty * level; if (minCapacity >= bytes.length) { ensureCapacity(minCapacity); } final byte[] bytes = this.bytes; bytes[off++] = (byte) '['; - if (pretty) { - indent++; - bytes[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - bytes[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } this.off = off; } @@ -312,18 +297,14 @@ public final void startArray() { public final void endArray() { level--; int off = this.off; - int minCapacity = off + (pretty ? 2 + indent : 1); + int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1); if (minCapacity >= bytes.length) { ensureCapacity(minCapacity); } final byte[] bytes = this.bytes; - if (pretty) { - indent--; - bytes[off++] = (byte) '\n'; - for (int i = 0; i < indent; ++i) { - bytes[off++] = (byte) '\t'; - } + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } bytes[off] = (byte) ']'; this.off = off + 1; @@ -331,6 +312,10 @@ public final void endArray() { } public final void writeString(List list) { + if (pretty != PRETTY_NON) { + super.writeString(list); + return; + } // startArray(); int off = this.off; if (off == bytes.length) { @@ -878,7 +863,7 @@ public final void writeString(final char[] chars, int stroff, int strlen) { } protected final void writeStringEscaped(byte[] values) { - int minCapacity = off + values.length * 4 + 2; + int minCapacity = off + values.length * 6 + 2; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1689,7 +1674,7 @@ public final void writeRaw(byte[] bytes) { @Override public final void writeNameRaw(byte[] name) { int off = this.off; - int minCapacity = off + name.length + 2 + indent; + int minCapacity = off + name.length + 2 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1698,8 +1683,8 @@ public final void writeNameRaw(byte[] name) { } else { final byte[] bytes = this.bytes; bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } System.arraycopy(name, 0, bytes, off, name.length); @@ -1709,7 +1694,7 @@ public final void writeNameRaw(byte[] name) { @Override public final void writeName2Raw(long name) { int off = this.off; - int minCapacity = off + 10 + indent; + int minCapacity = off + 10 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1719,8 +1704,8 @@ public final void writeName2Raw(long name) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1728,19 +1713,17 @@ public final void writeName2Raw(long name) { this.off = off + 5; } - private static int indent(byte[] bytes, int off, int indent) { - bytes[off++] = '\n'; - int end = off + indent; - while (off < end) { - bytes[off++] = '\t'; - } - return off; + private int indent(byte[] bytes, int off) { + bytes[off] = '\n'; + int toIndex = off + 1 + pretty * level; + Arrays.fill(bytes, off + 1, toIndex, pretty == PRETTY_TAB ? (byte) '\t' : (byte) ' '); + return toIndex; } @Override public final void writeName3Raw(long name) { int off = this.off; - int minCapacity = off + 10 + indent; + int minCapacity = off + 10 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1750,8 +1733,8 @@ public final void writeName3Raw(long name) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1762,7 +1745,7 @@ public final void writeName3Raw(long name) { @Override public final void writeName4Raw(long name) { int off = this.off; - int minCapacity = off + 10 + indent; + int minCapacity = off + 10 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1772,8 +1755,8 @@ public final void writeName4Raw(long name) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1784,7 +1767,7 @@ public final void writeName4Raw(long name) { @Override public final void writeName5Raw(long name) { int off = this.off; - int minCapacity = off + 10 + indent; + int minCapacity = off + 10 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1794,8 +1777,8 @@ public final void writeName5Raw(long name) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1806,7 +1789,7 @@ public final void writeName5Raw(long name) { @Override public final void writeName6Raw(long name) { int off = this.off; - int minCapacity = off + 11 + indent; + int minCapacity = off + 11 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1816,8 +1799,8 @@ public final void writeName6Raw(long name) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1829,7 +1812,7 @@ public final void writeName6Raw(long name) { @Override public final void writeName7Raw(long name) { int off = this.off; - int minCapacity = off + 12 + indent; + int minCapacity = off + 12 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1839,8 +1822,8 @@ public final void writeName7Raw(long name) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1853,7 +1836,7 @@ public final void writeName7Raw(long name) { @Override public final void writeName8Raw(long name) { int off = this.off; - int minCapacity = off + 13 + indent; + int minCapacity = off + 13 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1863,8 +1846,8 @@ public final void writeName8Raw(long name) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1878,7 +1861,7 @@ public final void writeName8Raw(long name) { @Override public final void writeName9Raw(long name0, int name1) { int off = this.off; - int minCapacity = off + 14 + indent; + int minCapacity = off + 14 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1888,8 +1871,8 @@ public final void writeName9Raw(long name0, int name1) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1901,7 +1884,7 @@ public final void writeName9Raw(long name0, int name1) { @Override public final void writeName10Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 18 + indent; + int minCapacity = off + 18 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1911,8 +1894,8 @@ public final void writeName10Raw(long name0, long name1) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1924,7 +1907,7 @@ public final void writeName10Raw(long name0, long name1) { @Override public final void writeName11Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 18 + indent; + int minCapacity = off + 18 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1934,8 +1917,8 @@ public final void writeName11Raw(long name0, long name1) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1947,7 +1930,7 @@ public final void writeName11Raw(long name0, long name1) { @Override public final void writeName12Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 18 + indent; + int minCapacity = off + 18 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1957,8 +1940,8 @@ public final void writeName12Raw(long name0, long name1) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1970,7 +1953,7 @@ public final void writeName12Raw(long name0, long name1) { @Override public final void writeName13Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 18 + indent; + int minCapacity = off + 18 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -1980,8 +1963,8 @@ public final void writeName13Raw(long name0, long name1) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -1993,7 +1976,7 @@ public final void writeName13Raw(long name0, long name1) { @Override public final void writeName14Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 19 + indent; + int minCapacity = off + 19 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -2003,8 +1986,8 @@ public final void writeName14Raw(long name0, long name1) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -2017,7 +2000,7 @@ public final void writeName14Raw(long name0, long name1) { @Override public final void writeName15Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 20 + indent; + int minCapacity = off + 20 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -2027,8 +2010,8 @@ public final void writeName15Raw(long name0, long name1) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -2042,7 +2025,7 @@ public final void writeName15Raw(long name0, long name1) { @Override public final void writeName16Raw(long name0, long name1) { int off = this.off; - int minCapacity = off + 21 + indent; + int minCapacity = off + 21 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -2052,8 +2035,8 @@ public final void writeName16Raw(long name0, long name1) { startObject = false; } else { bytes[off++] = ','; - if (pretty) { - off = indent(bytes, off, indent); + if (pretty != PRETTY_NON) { + off = indent(bytes, off); } } @@ -2097,7 +2080,7 @@ public final void writeRaw(char c0, char c1) { @Override public final void writeNameRaw(byte[] bytes, int off, int len) { - int minCapacity = this.off + len + 2 + indent; + int minCapacity = this.off + len + 2 + pretty * level; if (minCapacity >= this.bytes.length) { ensureCapacity(minCapacity); } @@ -2994,6 +2977,11 @@ public final void writeNameRaw(char[] bytes, int offset, int len) { @Override public final void write(JSONObject map) { + if (pretty != PRETTY_NON) { + super.write(map); + return; + } + if (map == null) { this.writeNull(); return; diff --git a/core/src/main/java/com/alibaba/fastjson2/codec/FieldInfo.java b/core/src/main/java/com/alibaba/fastjson2/codec/FieldInfo.java index 322300dfd..dd433293d 100644 --- a/core/src/main/java/com/alibaba/fastjson2/codec/FieldInfo.java +++ b/core/src/main/java/com/alibaba/fastjson2/codec/FieldInfo.java @@ -21,6 +21,7 @@ public class FieldInfo { public static final long DISABLE_AUTO_TYPE = 1L << 59; public static final long DISABLE_JSONB = 1L << 60; public static final long BACKR_EFERENCE = 1L << 61; + public static final long RECORD = 1L << 62; public String fieldName; public String format; diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReader.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReader.java index 8939aa58b..ef9336002 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReader.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReader.java @@ -613,9 +613,6 @@ private String getActualFieldName(FieldReader fieldReader) { } private boolean needCompareToActualFieldClass(Class clazz) { - if (clazz.isEnum() || clazz.isInterface()) { - return true; - } - return false; + return clazz.isEnum() || clazz.isInterface(); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderLocalDateTime.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderLocalDateTime.java index 930827197..070c42e72 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderLocalDateTime.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderLocalDateTime.java @@ -59,7 +59,7 @@ public boolean supportAcceptType(Class valueClass) { @Override public void readFieldValue(JSONReader jsonReader, Object object) { // 若使用的是JSONReaderJSONB则使用JSONReaderJSONB定义的时间反序列化方法 - LocalDateTime date = null; + LocalDateTime date; if (jsonReader.jsonb) { date = (LocalDateTime) dateReader.readJSONBObject(jsonReader, fieldType, fieldName, features); } else { diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapField.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapField.java index 637c4478f..166f61f0a 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapField.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapField.java @@ -68,7 +68,7 @@ public void readFieldValue(JSONReader jsonReader, T object) { array, arrayToMapKey, namingStrategy, - JSONFactory.getObjectReader(valueType, this.features | features), + JSONFactory.getObjectReader(valueType, features), arrayToMapDuplicateHandler); accept(object, map); return; diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapFieldReadOnly.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapFieldReadOnly.java index 21192ff53..1075b747d 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapFieldReadOnly.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapFieldReadOnly.java @@ -133,7 +133,7 @@ public void readFieldValue(JSONReader jsonReader, T object) { array, arrayToMapKey, namingStrategy, - JSONFactory.getObjectReader(valueType, this.features | features), + JSONFactory.getObjectReader(valueType, features), arrayToMapDuplicateHandler); return; } diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapMethod.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapMethod.java index f3f55a34b..fa8daedc3 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapMethod.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapMethod.java @@ -70,7 +70,7 @@ public void readFieldValue(JSONReader jsonReader, T object) { array, arrayToMapKey, namingStrategy, - JSONFactory.getObjectReader(valueType, this.features | features), + JSONFactory.getObjectReader(valueType, features), arrayToMapDuplicateHandler); accept(object, map); return; diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapMethodReadOnly.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapMethodReadOnly.java index 09ba3ee3a..447dc7ee7 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapMethodReadOnly.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderMapMethodReadOnly.java @@ -118,7 +118,7 @@ public void readFieldValue(JSONReader jsonReader, T object) { array, arrayToMapKey, namingStrategy, - JSONFactory.getObjectReader(valueType, this.features | features), + JSONFactory.getObjectReader(valueType, features), arrayToMapDuplicateHandler); return; } diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderNumberFunc.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderNumberFunc.java index 25a4373f1..92c9a7efc 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderNumberFunc.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderNumberFunc.java @@ -34,7 +34,7 @@ public void accept(T object, Object value) { } if (value instanceof Boolean) { - value = ((Boolean) value).booleanValue() ? 1 : 0; + value = (Boolean) value ? 1 : 0; } function.accept(object, (V) value); diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java index 63c1ac8d3..d38bf864b 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java @@ -436,7 +436,7 @@ protected final FieldReader getFieldReaderUL(long hashCode, JSONReader jsonReade if (fieldReader == null && jsonReader.isSupportSmartMatch(this.features | features)) { long hashCodeL = jsonReader.getNameHashCodeLCase(); - fieldReader = getFieldReaderLCase(hashCodeL == hashCode ? hashCode : hashCodeL); + fieldReader = getFieldReaderLCase(hashCodeL); } return fieldReader; } @@ -446,7 +446,7 @@ protected final void readFieldValue(long hashCode, JSONReader jsonReader, long f if (fieldReader == null && jsonReader.isSupportSmartMatch(this.features | features)) { long hashCodeL = jsonReader.getNameHashCodeLCase(); - fieldReader = getFieldReaderLCase(hashCodeL == hashCode ? hashCode : hashCodeL); + fieldReader = getFieldReaderLCase(hashCodeL); } if (fieldReader != null) { diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderBaseModule.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderBaseModule.java index 1c1bbbe89..639dc1e73 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderBaseModule.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderBaseModule.java @@ -562,8 +562,7 @@ void getBeanInfo1x(BeanInfo beanInfo, Annotation annotation) { }); } - private void processBuilder(BeanInfo beanInfo, Class result) { - Class builderClass = result; + private void processBuilder(BeanInfo beanInfo, Class builderClass) { if (builderClass != void.class && builderClass != Void.class) { beanInfo.builder = builderClass; diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreatorASM.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreatorASM.java index ee988a06c..8d93714d3 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreatorASM.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreatorASM.java @@ -3032,7 +3032,9 @@ private int genReadFieldValue( if (list) { Class itemClass = TypeUtils.getMapping(itemType); - if (itemClass != null && Collection.class.isAssignableFrom(itemClass)) { + if (itemClass != null + && (Collection.class.isAssignableFrom(itemClass) || !Modifier.isPublic(itemClass.getModifiers())) + ) { list = false; } } diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplEnum.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplEnum.java index bf5d53bc3..0abbc966e 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplEnum.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplEnum.java @@ -49,9 +49,8 @@ public ObjectReaderImplEnum( this.valueFieldType = valueFieldType; if (valueFieldType != null) { - if (valueFieldType == String.class) { - stringValues = new String[enums.length]; - } else { + stringValues = new String[enums.length]; + if (valueFieldType != String.class) { intValues = new long[enums.length]; } @@ -67,8 +66,11 @@ public ObjectReaderImplEnum( if (valueFieldType == String.class) { stringValues[i] = (String) fieldValue; - } else if (fieldValue instanceof Number) { - intValues[i] = ((Number) fieldValue).longValue(); + } else { + stringValues[i] = fieldValue == null ? null : fieldValue.toString(); + if (fieldValue instanceof Number) { + intValues[i] = ((Number) fieldValue).longValue(); + } } } catch (Exception ignored) { // ignored diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplList.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplList.java index 78776618c..ee01e1825 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplList.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplList.java @@ -46,7 +46,7 @@ public final class ObjectReaderImplList volatile Constructor constructor; public static ObjectReader of(Type type, Class listClass, long features) { - if (listClass == type && "".equals(listClass.getSimpleName())) { + if (listClass == type && listClass.getSimpleName().isEmpty()) { type = listClass.getGenericSuperclass(); listClass = listClass.getSuperclass(); } @@ -149,6 +149,8 @@ public static ObjectReader of(Type type, Class listClass, long features) { builder = GuavaSupport.immutableSetConverter(); break; case "com.google.common.collect.Lists$TransformingRandomAccessList": + case "java.util.RandomAccessSubList": + case "java.util.AbstractList$RandomAccessSubList": instanceClass = ArrayList.class; break; case "com.google.common.collect.Lists.TransformingSequentialList": @@ -174,10 +176,6 @@ public static ObjectReader of(Type type, Class listClass, long features) { instanceClass = TreeSet.class; builder = (Function) Collections::synchronizedNavigableSet; break; - case "java.util.RandomAccessSubList": - case "java.util.AbstractList$RandomAccessSubList": - instanceClass = ArrayList.class; - break; default: instanceClass = listClass; } @@ -524,10 +522,10 @@ public Object readJSONBObject(JSONReader jsonReader, Type fieldType, Object fiel } else if (listType != null && listType != this.listType) { switch (listType.getName()) { case "kotlin.collections.EmptySet": - list = (Collection) getKotlinEmptySet(listType); + list = getKotlinEmptySet(listType); break; case "kotlin.collections.EmptyList": - list = (Collection) getKotlinEmptyList(listType); + list = getKotlinEmptyList(listType); break; default: try { diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplListStr.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplListStr.java index 90c7b8296..783404180 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplListStr.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplListStr.java @@ -16,7 +16,6 @@ public final class ObjectReaderImplListStr implements ObjectReader { final Class listType; final Class instanceType; - Object listSingleton; public ObjectReaderImplListStr(Class listType, Class instanceType) { this.listType = listType; diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplMap.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplMap.java index de4f844aa..9e928fcb0 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplMap.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplMap.java @@ -44,7 +44,7 @@ public static ObjectReader of(Type fieldType, Class mapType, long features) { Function builder = null; Class instanceType = mapType; - if ("".equals(instanceType.getSimpleName())) { + if (instanceType.getSimpleName().isEmpty()) { instanceType = mapType.getSuperclass(); if (fieldType == null) { fieldType = mapType.getGenericSuperclass(); diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplObject.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplObject.java index d23a43834..829f37a06 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplObject.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplObject.java @@ -102,8 +102,8 @@ public Object readObject(JSONReader jsonReader, Type fieldType, Object fieldName contextClass = classLoader.loadClass(typeName); } catch (ClassNotFoundException ignored) { } - - if (!objectClass.equals(contextClass)) { + //明确contextClass类型与objectClass不一致时才更改reader + if (contextClass != null && !objectClass.equals(contextClass)) { autoTypeObjectReader = context.getObjectReader(contextClass); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderNoneDefaultConstructor.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderNoneDefaultConstructor.java index fd27f29c1..e12d65b9a 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderNoneDefaultConstructor.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderNoneDefaultConstructor.java @@ -1,9 +1,6 @@ package com.alibaba.fastjson2.reader; -import com.alibaba.fastjson2.JSONB; -import com.alibaba.fastjson2.JSONException; -import com.alibaba.fastjson2.JSONFactory; -import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.*; import com.alibaba.fastjson2.util.Fnv; import com.alibaba.fastjson2.util.TypeUtils; @@ -471,9 +468,16 @@ typeName, getObjectClass(), features | getFeatures() Class valueClass = fieldValue.getClass(); Class fieldClass = fieldReader.fieldClass; if (valueClass != fieldClass) { - Function typeConvert = provider.getTypeConvert(valueClass, fieldClass); - if (typeConvert != null) { - fieldValue = typeConvert.apply(fieldValue); + if (fieldValue instanceof JSONObject) { + ObjectReader fieldObjectReader = provider.getObjectReader(fieldReader.fieldType); + fieldValue = fieldObjectReader.createInstance((Map) fieldValue, features); + } else if (fieldValue instanceof JSONArray) { + fieldValue = ((JSONArray) fieldValue).to(fieldReader.fieldType, features); + } else { + Function typeConvert = provider.getTypeConvert(valueClass, fieldClass); + if (typeConvert != null) { + fieldValue = typeConvert.apply(fieldValue); + } } } } diff --git a/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriter.java b/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriter.java index cdccdb3c4..8b29a2301 100644 --- a/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriter.java +++ b/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriter.java @@ -226,7 +226,7 @@ public void writeBigInteger(BigInteger value) { public final void writeDate(long millis) { ZoneId zoneId = this.zoneId; - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(millis, 1000L); int offsetTotalSeconds; if (zoneId == DateUtils.SHANGHAI_ZONE_ID || zoneId.getRules() == DateUtils.SHANGHAI_ZONE_RULES) { @@ -237,8 +237,8 @@ public final void writeDate(long millis) { } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; diff --git a/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF16.java b/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF16.java index b715877b7..872450822 100644 --- a/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF16.java +++ b/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF16.java @@ -43,23 +43,17 @@ void writeDirect(char[] bytes, int off, int len) { } public void writeComma() { - if (off + 1 == chars.length) { - flush(); - } + checkCapacity(1); chars[off++] = ','; } protected void writeQuote() { - if (off + 1 == chars.length) { - flush(); - } + checkCapacity(1); chars[off++] = '"'; } public void writeLine() { - if (off + 1 == chars.length) { - flush(); - } + checkCapacity(1); chars[off++] = '\n'; } @@ -69,19 +63,12 @@ public void writeBoolean(boolean booleanValue) { } public void writeInt64(long longValue) { - int minCapacity = off + 21; - if (minCapacity - this.chars.length > 0) { - flush(); - } - + checkCapacity(20); off = IOUtils.writeInt64(chars, off, longValue); } public void writeDateYYYMMDD10(int year, int month, int dayOfMonth) { - if (off + 11 >= this.chars.length) { - flush(); - } - + checkCapacity(10); off = IOUtils.writeLocalDate(chars, off, year, month, dayOfMonth); } @@ -92,9 +79,7 @@ public void writeDateTime19( int hour, int minute, int second) { - if (off + 20 >= this.chars.length) { - flush(); - } + checkCapacity(19); final char[] chars = this.chars; int off = this.off; @@ -108,7 +93,7 @@ public void writeDateTime19( this.off = off + 9; } - public void writeString(String str) { + public void writeString(final String str) { if (str == null || str.isEmpty()) { return; } @@ -129,7 +114,7 @@ public void writeString(String str) { char ch = str.charAt(i); if (ch == ',') { comma = true; - } else if (ch == '"') { + } else if (ch == '"' || ch == '\n' || ch == '\r') { escapeCount++; } } @@ -138,53 +123,61 @@ public void writeString(String str) { } } - if (escapeCount == 0) { - str.getChars(0, str.length(), chars, off); - off += str.length(); + if (escapeCount == 0 && !comma) { + if (len + off >= chars.length) { + flush(); + if (len > chars.length) { + try { + out.write(str); + } catch (IOException e) { + throw new JSONException("write csv error", e); + } + return; + } + } + str.getChars(0, len, chars, off); + off += len; return; } - if (off + 2 + str.length() + escapeCount >= chars.length) { - flush(); - } + checkCapacity(2 + len + escapeCount); + // 利用本地局部变量,可以提高遍历速度 + final char[] chars = this.chars; + final int max = chars.length - 2; + int off = this.off; chars[off++] = '"'; - for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); + for (int i = 0; i < len; ) { + char ch = str.charAt(i++); if (ch == '"') { chars[off++] = '"'; chars[off++] = '"'; } else { chars[off++] = ch; } + if (off >= max) { + flush(); + off = this.off; + } } chars[off++] = '"'; + this.off = off; } public void writeInt32(int intValue) { - int minCapacity = off + 11; - if (minCapacity - this.chars.length > 0) { - flush(); - } - + checkCapacity(11); off = IOUtils.writeInt32(chars, off, intValue); } public void writeDouble(double value) { - int minCapacity = off + 24; - if (minCapacity - this.chars.length > 0) { - flush(); - } + checkCapacity(24); int size = DoubleToDecimal.toString(value, this.chars, off, true); off += size; } public void writeFloat(float value) { - int minCapacity = off + 15; - if (minCapacity - this.chars.length > 0) { - flush(); - } + checkCapacity(15); int size = DoubleToDecimal.toString(value, this.chars, off, true); off += size; @@ -205,7 +198,7 @@ public void writeString(byte[] utf8) { return; } - String str = new String(utf8, 0, utf8.length, StandardCharsets.UTF_8); + String str = new String(utf8, StandardCharsets.UTF_8); writeString(str); } @@ -217,10 +210,7 @@ public void writeDecimal(BigDecimal value) { String str = value.toString(); int strlen = str.length(); - int minCapacity = off + 24; - if (minCapacity - this.chars.length > 0) { - flush(); - } + checkCapacity(24); str.getChars(0, strlen, chars, off); off += strlen; @@ -237,10 +227,7 @@ public void writeDecimal(long unscaledVal, int scale) { return; } - int minCapacity = off + 24; - if (minCapacity - this.chars.length > 0) { - flush(); - } + checkCapacity(24); off = IOUtils.writeDecimal(chars, off, unscaledVal, scale); } @@ -265,6 +252,9 @@ public void writeLocalDateTime(LocalDateTime ldt) { return; } + // "yyyy-MM-dd HH:mm:ss" + checkCapacity(19); + off = IOUtils.writeLocalDate(chars, off, ldt.getYear(), ldt.getMonthValue(), ldt.getDayOfMonth()); chars[off++] = ' '; off = IOUtils.writeLocalTime(chars, off, ldt.toLocalTime()); @@ -274,14 +264,18 @@ protected void writeRaw(String str) { if (str == null || str.isEmpty()) { return; } + checkCapacity(str.length()); - if (str.length() + off >= chars.length) { - flush(); - } str.getChars(0, str.length(), this.chars, off); off += str.length(); } + void checkCapacity(int incr) { + if (off + incr >= chars.length) { + flush(); + } + } + @Override public void close() throws IOException { if (off > 0) { diff --git a/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF8.java index 88de07c31..f31c67ef4 100644 --- a/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF8.java @@ -46,23 +46,17 @@ void writeDirect(byte[] bytes, int off, int len) { } public void writeComma() { - if (off + 1 == bytes.length) { - flush(); - } + checkCapacity(1); bytes[off++] = ','; } protected void writeQuote() { - if (off + 1 == bytes.length) { - flush(); - } + checkCapacity(1); bytes[off++] = '"'; } public void writeLine() { - if (off + 1 == bytes.length) { - flush(); - } + checkCapacity(1); bytes[off++] = '\n'; } @@ -72,18 +66,13 @@ public void writeBoolean(boolean booleanValue) { } public void writeInt64(long longValue) { - int minCapacity = off + 21; - if (minCapacity - this.bytes.length > 0) { - flush(); - } + checkCapacity(20); // -9223372036854775808 off = IOUtils.writeInt64(bytes, off, longValue); } public void writeDateYYYMMDD10(int year, int month, int dayOfMonth) { - if (off + 11 >= this.bytes.length) { - flush(); - } + checkCapacity(10); off = IOUtils.writeLocalDate(bytes, off, year, month, dayOfMonth); } @@ -96,9 +85,7 @@ public void writeDateTime19( int minute, int second ) { - if (off + 20 >= this.bytes.length) { - flush(); - } + checkCapacity(19); final byte[] bytes = this.bytes; int off = this.off; @@ -125,29 +112,20 @@ public void writeString(String value) { } public void writeInt32(int intValue) { - int minCapacity = off + 11; - if (minCapacity - this.bytes.length > 0) { - flush(); - } + checkCapacity(11); // -2147483648 off = IOUtils.writeInt32(bytes, off, intValue); } public void writeDouble(double value) { - int minCapacity = off + 24; - if (minCapacity - this.bytes.length > 0) { - flush(); - } + checkCapacity(24); int size = DoubleToDecimal.toString(value, this.bytes, off, true); off += size; } public void writeFloat(float value) { - int minCapacity = off + 15; - if (minCapacity - this.bytes.length > 0) { - flush(); - } + checkCapacity(15); int size = DoubleToDecimal.toString(value, this.bytes, off, true); off += size; @@ -182,7 +160,7 @@ public void writeString(byte[] utf8) { for (byte ch : utf8) { if (ch == ',') { comma = true; - } else if (ch == '"') { + } else if (ch == '"' || ch == '\n' || ch == '\r') { escapeCount++; } } @@ -191,14 +169,17 @@ public void writeString(byte[] utf8) { } } - if (escapeCount == 0) { + if (escapeCount == 0 && !comma) { writeRaw(utf8); return; } - if (off + 2 + utf8.length + escapeCount >= bytes.length) { - flush(); - } + checkCapacity(2 + len + escapeCount); + + // 利用本地局部变量,可以提高遍历速度 + final byte[] bytes = this.bytes; + final int max = bytes.length - 2; + int off = this.off; bytes[off++] = '"'; for (byte ch : utf8) { @@ -208,8 +189,13 @@ public void writeString(byte[] utf8) { } else { bytes[off++] = ch; } + if (off >= max) { + flush(); + off = this.off; + } } bytes[off++] = '"'; + this.off = off; } public void writeDecimal(BigDecimal value) { @@ -220,10 +206,7 @@ public void writeDecimal(BigDecimal value) { String str = value.toString(); int strlen = str.length(); - int minCapacity = off + 24; - if (minCapacity - this.bytes.length > 0) { - flush(); - } + checkCapacity(24); str.getBytes(0, strlen, bytes, off); off += strlen; @@ -240,10 +223,7 @@ public void writeDecimal(long unscaledVal, int scale) { return; } - int minCapacity = off + 24; - if (minCapacity - this.bytes.length > 0) { - flush(); - } + checkCapacity(24); off = IOUtils.writeDecimal(bytes, off, unscaledVal, scale); } @@ -269,18 +249,7 @@ protected void writeRaw(String str) { } byte[] strBytes = str.getBytes(charset); - if (strBytes.length + off < bytes.length) { - System.arraycopy(strBytes, 0, bytes, off, strBytes.length); - off += strBytes.length; - } else { - flush(); - if (strBytes.length >= bytes.length) { - writeDirect(strBytes, 0, strBytes.length); - } else { - System.arraycopy(strBytes, 0, bytes, off, strBytes.length); - off += strBytes.length; - } - } + writeRaw(strBytes); } public void writeLocalDateTime(LocalDateTime ldt) { @@ -288,11 +257,20 @@ public void writeLocalDateTime(LocalDateTime ldt) { return; } + // "yyyy-MM-dd HH:mm:ss" + checkCapacity(19); + off = IOUtils.writeLocalDate(bytes, off, ldt.getYear(), ldt.getMonthValue(), ldt.getDayOfMonth()); bytes[off++] = ' '; off = IOUtils.writeLocalTime(bytes, off, ldt.toLocalTime()); } + void checkCapacity(int incr) { + if (off + incr >= bytes.length) { + flush(); + } + } + @Override public void close() throws IOException { if (off > 0) { diff --git a/core/src/main/java/com/alibaba/fastjson2/util/BeanUtils.java b/core/src/main/java/com/alibaba/fastjson2/util/BeanUtils.java index 0abb287a2..796e33402 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/BeanUtils.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/BeanUtils.java @@ -2828,23 +2828,27 @@ public static void processJacksonJsonFormat(BeanInfo beanInfo, Annotation annota String name = m.getName(); try { Object result = m.invoke(annotation); - if ("pattern".equals(name)) { - String pattern = (String) result; - if (!pattern.isEmpty()) { - beanInfo.format = pattern; - } - } else if ("shape".equals(name)) { - String shape = ((Enum) result).name(); - if ("NUMBER".equals(shape)) { - beanInfo.format = "millis"; - } else if ("OBJECT".equals(shape)) { - beanInfo.writeEnumAsJavaBean = true; - } - } else if ("locale".equals(name)) { - String locale = (String) result; - if (!locale.isEmpty() && !"##default".equals(locale)) { - beanInfo.locale = Locale.forLanguageTag(locale); - } + switch (name) { + case "pattern": + String pattern = (String) result; + if (!pattern.isEmpty()) { + beanInfo.format = pattern; + } + break; + case "shape": + String shape = ((Enum) result).name(); + if ("NUMBER".equals(shape)) { + beanInfo.format = "millis"; + } else if ("OBJECT".equals(shape)) { + beanInfo.writeEnumAsJavaBean = true; + } + break; + case "locale": + String locale = (String) result; + if (!locale.isEmpty() && !"##default".equals(locale)) { + beanInfo.locale = Locale.forLanguageTag(locale); + } + break; } } catch (Throwable ignored) { // ignored @@ -3003,7 +3007,7 @@ public static boolean isExtendedMap(Class objectClass) { if (objectClass == HashMap.class || objectClass == LinkedHashMap.class || objectClass == TreeMap.class - || "".equals(objectClass.getSimpleName()) + || objectClass.getSimpleName().isEmpty() ) { return false; } diff --git a/core/src/main/java/com/alibaba/fastjson2/util/DateUtils.java b/core/src/main/java/com/alibaba/fastjson2/util/DateUtils.java index 9ad1ad9b6..edcdf2139 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/DateUtils.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/DateUtils.java @@ -41,7 +41,7 @@ public class DateUtils { static { final long timeMillis = System.currentTimeMillis(); ZoneId zoneId = DEFAULT_ZONE_ID; - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(timeMillis, 1000L); int offsetTotalSeconds; @@ -53,7 +53,7 @@ public class DateUtils { } long localSecond = epochSecond + offsetTotalSeconds; - LOCAL_EPOCH_DAY = (int) Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); + LOCAL_EPOCH_DAY = (int) Math.floorDiv(localSecond, SECONDS_PER_DAY); } static class CacheDate8 { @@ -10924,7 +10924,7 @@ public static String formatYMDHMS19(Date date, ZoneId zoneId) { zoneId = DEFAULT_ZONE_ID; } - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(timeMillis, 1000L); int offsetTotalSeconds; @@ -10939,8 +10939,8 @@ public static String formatYMDHMS19(Date date, ZoneId zoneId) { } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; @@ -11029,7 +11029,7 @@ public static String formatYMD8(Date date) { } public static String formatYMD8(long timeMillis, ZoneId zoneId) { - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(timeMillis, 1000L); int offsetTotalSeconds; @@ -11045,7 +11045,7 @@ public static String formatYMD8(long timeMillis, ZoneId zoneId) { } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); int off = (int) (localEpochDay - LOCAL_EPOCH_DAY + 128); @@ -11199,7 +11199,7 @@ public static String formatYMD10(long timeMillis, ZoneId zoneId) { zoneId = DEFAULT_ZONE_ID; } - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(timeMillis, 1000L); int offsetTotalSeconds; @@ -11211,7 +11211,7 @@ public static String formatYMD10(long timeMillis, ZoneId zoneId) { } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); int off = (int) (localEpochDay - LOCAL_EPOCH_DAY + 128); final String[] cache = CacheDate10.CACHE; @@ -11572,7 +11572,7 @@ public static String format(Date date) { public static String format(long timeMillis, DateTimeFormatPattern pattern) { ZoneId zoneId = DEFAULT_ZONE_ID; - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(timeMillis, 1000L); int offsetTotalSeconds; @@ -11584,8 +11584,8 @@ public static String format(long timeMillis, DateTimeFormatPattern pattern) { } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; @@ -11733,7 +11733,7 @@ public static String toString(Date date) { } public static String toString(long timeMillis, boolean timeZone, ZoneId zoneId) { - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(timeMillis, 1000L); int offsetTotalSeconds; @@ -11745,8 +11745,8 @@ public static String toString(long timeMillis, boolean timeZone, ZoneId zoneId) } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; diff --git a/core/src/main/java/com/alibaba/fastjson2/util/FDBigInteger.java b/core/src/main/java/com/alibaba/fastjson2/util/FDBigInteger.java index 14934f279..e90ed00ad 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/FDBigInteger.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/FDBigInteger.java @@ -267,14 +267,13 @@ public FDBigInteger leftShift(int shift) { int prev = data[idx]; int hi = prev >>> anticount; int[] result = data; - int[] src = data; if (hi != 0) { if (nWords == data.length) { this.data = result = new int[nWords + 1]; } result[nWords++] = hi; } - leftShift(src, idx, result, bitcount, anticount, prev); + leftShift(data, idx, result, bitcount, anticount, prev); } } this.nWords = nWords; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriter.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriter.java index 1777ac763..36d48201d 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriter.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriter.java @@ -707,7 +707,7 @@ public void writeDate(JSONWriter jsonWriter, boolean writeFieldName, long millis return; } - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; JSONWriter.Context ctx = jsonWriter.context; if (isDateFormatMillis() || ctx.isDateFormatMillis()) { @@ -729,8 +729,8 @@ public void writeDate(JSONWriter jsonWriter, boolean writeFieldName, long millis .getOffset(instant); long localSecond = epochSecond + offset.getTotalSeconds(); - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterDate.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterDate.java index 3037e7cfd..b65027401 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterDate.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterDate.java @@ -121,7 +121,7 @@ public void writeDate(JSONWriter jsonWriter, long timeMillis) { return; } - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; JSONWriter.Context ctx = jsonWriter.context; @@ -154,8 +154,8 @@ public void writeDate(JSONWriter jsonWriter, long timeMillis) { } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java index 4be231efa..4d02e5a25 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java @@ -241,7 +241,16 @@ public void writeEnumJSONB(JSONWriter jsonWriter, Enum e) { @Override public boolean write(JSONWriter jsonWriter, T object) { - long features = this.features | jsonWriter.getFeatures(); + JSONWriter.Context context = jsonWriter.context; + long oldFeatures = context.getFeatures(); + context.setFeatures(this.features | oldFeatures); + boolean result = writeInternal(jsonWriter, object); + context.setFeatures(oldFeatures); + return result; + } + + private boolean writeInternal(JSONWriter jsonWriter, T object) { + long features = jsonWriter.getFeatures(); if (!fieldClassSerializable && (features & JSONWriter.Feature.IgnoreNoneSerializable.mask) != 0) { return false; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterBaseModule.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterBaseModule.java index 9fd8871f0..899fcacd8 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterBaseModule.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterBaseModule.java @@ -726,6 +726,9 @@ private void applyFeatures(FieldInfo fieldInfo, Enum[] features) { case "WriteBigDecimalAsPlain": fieldInfo.features |= JSONWriter.Feature.WriteBigDecimalAsPlain.mask; break; + case "DisableCircularReferenceDetect": + fieldInfo.features |= FieldInfo.DISABLE_REFERENCE_DETECT; + break; default: break; } diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java index 86e58c5df..713f753f7 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java @@ -479,6 +479,20 @@ boolean record = BeanUtils.isRecord(objectClass); if (origin != null && origin.compareTo(fieldWriter) > 0) { fieldWriterMap.put(fieldName, fieldWriter); } + + // the sameFieldName means only differ in first character that one is upper case the other is lower case + if (origin == null) { + String sameFieldName = null; + char firstChar = fieldName.charAt(0); + if (firstChar >= 'A' && firstChar <= 'Z') { + sameFieldName = (char) (firstChar + 32) + fieldName.substring(1); + } else if (firstChar >= 'a' && firstChar <= 'z') { + sameFieldName = (char) (firstChar - 32) + fieldName.substring(1); + } + if (sameFieldName != null && fieldWriterMap.containsKey(sameFieldName)) { + fieldWriterMap.remove(sameFieldName); + } + } }); fieldWriters = new ArrayList<>(fieldWriterMap.values()); @@ -927,7 +941,9 @@ public FieldWriter createFieldWriter( fieldName = BeanUtils.getterName(method, false, null); } - Field field = BeanUtils.getField(objectType, method); + Field field = (features & FieldInfo.RECORD) != 0 + ? null + : BeanUtils.getField(objectType, method); if (fieldClass == boolean.class || fieldClass == Boolean.class) { return new FieldWriterBoolMethod(fieldName, ordinal, features, format, label, field, method, fieldClass); @@ -1310,6 +1326,13 @@ protected ObjectWriter getInitWriter(ObjectWriterProvider provider, Class fieldC return objectWriter; } } + } else if (fieldClass == int.class || fieldClass == Integer.class) { + if ((provider.userDefineMask & ObjectWriterProvider.TYPE_INT32_MASK) != 0) { + ObjectWriter objectWriter = provider.cache.get(Integer.class); + if (objectWriter != ObjectWriterImplInt32.INSTANCE) { + return objectWriter; + } + } } else if (fieldClass == long.class || fieldClass == Long.class) { if ((provider.userDefineMask & ObjectWriterProvider.TYPE_INT64_MASK) != 0) { ObjectWriter objectWriter = provider.cache.get(Long.class); diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java index fce84bc1a..92d22a4d1 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java @@ -179,7 +179,8 @@ public ObjectWriter createObjectWriter( beanFeatures &= ~JSONWriter.Feature.WriteClassName.mask; } - long writerFieldFeatures = features | beanFeatures; + boolean record = BeanUtils.isRecord(objectClass); + long writerFieldFeatures = features | beanFeatures | (record ? FieldInfo.RECORD : 0); final boolean fieldBased = ((writerFieldFeatures & JSONWriter.Feature.FieldBased.mask) != 0 && !objectClass.isInterface()) || !beanInfo.alphabetic; @@ -190,8 +191,6 @@ public ObjectWriter createObjectWriter( return super.createObjectWriter(objectClass, features, provider); } - boolean record = BeanUtils.isRecord(objectClass); - List fieldWriters; Map fieldWriterMap = new LinkedHashMap<>(); if (!fieldBased || record) { @@ -346,6 +345,20 @@ boolean record = BeanUtils.isRecord(objectClass); if (origin != null && origin.compareTo(fieldWriter) > 0) { fieldWriterMap.put(fieldName, fieldWriter); } + + // the sameFieldName means only differ in first character that one is upper case the other is lower case + if (origin == null) { + String sameFieldName = null; + char firstChar = fieldName.charAt(0); + if (firstChar >= 'A' && firstChar <= 'Z') { + sameFieldName = (char) (firstChar + 32) + fieldName.substring(1); + } else if (firstChar >= 'a' && firstChar <= 'z') { + sameFieldName = (char) (firstChar - 32) + fieldName.substring(1); + } + if (sameFieldName != null && fieldWriterMap.containsKey(sameFieldName)) { + fieldWriterMap.remove(sameFieldName); + } + } }); } } else { @@ -1089,7 +1102,7 @@ private void gwListJSONB( int REF_PATH = mwc.var("REF_PATH"); boolean listSimple = false; - Type itemType = null; + Type itemType; Class itemClass = null; if (fieldType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) fieldType; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplDate.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplDate.java index 35398448d..3c714a6b1 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplDate.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplDate.java @@ -111,7 +111,7 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f } if (dateFormat == null) { - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; long epochSecond = Math.floorDiv(millis, 1000L); int offsetTotalSeconds; @@ -123,8 +123,8 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplDouble.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplDouble.java index 19db92e58..d4430b1f1 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplDouble.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplDouble.java @@ -22,7 +22,7 @@ public void writeJSONB(JSONWriter jsonWriter, Object object, Object fieldName, T return; } - double value = ((Double) object).doubleValue(); + double value = (Double) object; if ((features & JSONWriter.Feature.WriteNonStringValueAsString.mask) != 0) { jsonWriter.writeString(value); } else { @@ -51,7 +51,7 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f return; } - double value = ((Double) object).doubleValue(); + double value = (Double) object; if ((features & JSONWriter.Feature.WriteNonStringValueAsString.mask) != 0) { jsonWriter.writeString(value); return; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplFloat.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplFloat.java index 303478daf..00491a0b6 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplFloat.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplFloat.java @@ -22,7 +22,7 @@ public void writeJSONB(JSONWriter jsonWriter, Object object, Object fieldName, T return; } - float value = ((Float) object).floatValue(); + float value = (Float) object; if ((features & JSONWriter.Feature.WriteNonStringValueAsString.mask) != 0) { jsonWriter.writeString(value); } else { @@ -43,7 +43,7 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f return; } - float value = ((Float) object).floatValue(); + float value = (Float) object; if ((features & JSONWriter.Feature.WriteNonStringValueAsString.mask) != 0) { jsonWriter.writeString(value); return; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInstant.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInstant.java index c2558caf4..6cb040c03 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInstant.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInstant.java @@ -43,7 +43,7 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f boolean yyyyMMddhhmmss19 = this.yyyyMMddhhmmss19 || (context.isFormatyyyyMMddhhmmss19() && this.format == null); if (yyyyMMddhhmmss14 || yyyyMMddhhmmss19 || yyyyMMdd8 || yyyyMMdd10) { - final int SECONDS_PER_DAY = 60 * 60 * 24; + final long SECONDS_PER_DAY = 60 * 60 * 24; ZoneId zoneId = context.getZoneId(); long epochSecond = instant.getEpochSecond(); int offsetTotalSeconds; @@ -54,8 +54,8 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f } long localSecond = epochSecond + offsetTotalSeconds; - long localEpochDay = Math.floorDiv(localSecond, (long) SECONDS_PER_DAY); - int secsOfDay = (int) Math.floorMod(localSecond, (long) SECONDS_PER_DAY); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); int year, month, dayOfMonth; { final int DAYS_PER_CYCLE = 146097; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt32Array.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt32Array.java index 49eadd3da..7f5e3a2f3 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt32Array.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt32Array.java @@ -40,7 +40,7 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f continue; } - int value = item.intValue(); + int value = item; if (writeAsString) { jsonWriter.writeString(value); } else { @@ -66,13 +66,12 @@ public void writeJSONB(JSONWriter jsonWriter, Object object, Object fieldName, T Integer[] array = (Integer[]) object; jsonWriter.startArray(array.length); - for (int i = 0; i < array.length; i++) { - Integer item = array[i]; + for (Integer item : array) { if (item == null) { jsonWriter.writeNull(); continue; } - int value = item.intValue(); + int value = item; if (writeAsString) { jsonWriter.writeString(value); } else { diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt64Array.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt64Array.java index 090a37be5..d8b7a0b0a 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt64Array.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt64Array.java @@ -42,7 +42,7 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f continue; } - long longValue = item.longValue(); + long longValue = item; if (writeAsString) { jsonWriter.writeString(longValue); } else { @@ -74,7 +74,7 @@ public void writeJSONB(JSONWriter jsonWriter, Object object, Object fieldName, T continue; } - long longValue = item.longValue(); + long longValue = item; if (writeAsString) { jsonWriter.writeString(longValue); } else { diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt8Array.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt8Array.java index fa6dbe2f5..281618317 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt8Array.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplInt8Array.java @@ -34,7 +34,7 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f if (value == null) { jsonWriter.writeNull(); } else { - byte byteValue = value.byteValue(); + byte byteValue = value; if (writeAsString) { jsonWriter.writeString(byteValue); } else { @@ -60,12 +60,11 @@ public void writeJSONB(JSONWriter jsonWriter, Object object, Object fieldName, T Byte[] array = (Byte[]) object; jsonWriter.startArray(array.length); - for (int i = 0; i < array.length; i++) { - Byte value = array[i]; + for (Byte value : array) { if (value == null) { jsonWriter.writeNull(); } else { - byte byteValue = value.byteValue(); + byte byteValue = value; if (writeAsString) { jsonWriter.writeString(byteValue); } else { diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplList.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplList.java index c275fe161..195b3e21b 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplList.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplList.java @@ -267,8 +267,14 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f JSONWriter.Context context = jsonWriter.context; ObjectWriterProvider provider = context.provider; + int size = list.size(); + if (size == 0) { + jsonWriter.writeRaw('[', ']'); + return; + } + jsonWriter.startArray(); - for (int i = 0; i < list.size(); i++) { + for (int i = 0; i < size; i++) { if (i != 0) { jsonWriter.writeComma(); } @@ -376,9 +382,8 @@ private List getList(Object object) { } else if (object instanceof Iterable) { final Iterable items = (Iterable) object; List list = items instanceof Collection ? new ArrayList(((Collection) items).size()) : new ArrayList(); - Iterator iterator = items.iterator(); - while (iterator.hasNext()) { - list.add(iterator.next()); + for (Object item : items) { + list.add(item); } return list; } else { diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java index 6c00234b2..fc76b0e91 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java @@ -404,19 +404,21 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f writeWithFilter(jsonWriter, object, fieldName, fieldType, features); return; } + Map map = (Map) object; boolean refDetect = jsonWriter.isRefDetect(); + boolean writeTypeInfo = (fieldType == this.objectType && jsonWriter.isWriteMapTypeInfo(object, objectClass, features)) + || jsonWriter.isWriteTypeInfo(object, fieldType, features); + if (!writeTypeInfo && map.isEmpty()) { + jsonWriter.writeRaw('{', '}'); + return; + } jsonWriter.startObject(); - - if ((fieldType == this.objectType && jsonWriter.isWriteMapTypeInfo(object, objectClass, features)) - || jsonWriter.isWriteTypeInfo(object, fieldType, features) - ) { + if (writeTypeInfo) { writeTypeInfo(jsonWriter); } - Map map = (Map) object; - features |= jsonWriter.getFeatures(); if ((features & (MapSortField.mask | SortMapEntriesByKeys.mask)) != 0) { if (!(map instanceof SortedMap) @@ -619,9 +621,8 @@ public void writeWithFilter(JSONWriter jsonWriter, Object object, Object fieldNa key = entryKey.toString(); } - String refPath = null; if (refDetect) { - refPath = jsonWriter.setPath(key, value); + String refPath = jsonWriter.setPath(key, value); if (refPath != null) { jsonWriter.writeName(key); jsonWriter.writeColon(); diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterTest.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterTest.java index 221e4d9c9..7e998b4a3 100644 --- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterTest.java @@ -1010,6 +1010,17 @@ public void writeObject2() { } } + @Test + public void writeObjectNonStringKey() { + JSONObject object = new JSONObject(); + ((Map) object).put(123, "123"); + JSONWriter jsonWriter = JSONWriter.ofUTF8(PrettyFormat); + jsonWriter.write(object); + assertEquals("{\n" + + "\t123:\"123\"\n" + + "}", jsonWriter.toString()); + } + @Test public void writeString1() { char[] chars = "01234567890".toCharArray(); @@ -2863,4 +2874,33 @@ public void setFeatures() { context.setFeatures(features); assertEquals(features, context.getFeatures()); } + + @Test + public void writeNull() { + { + JSONWriter jsonWriter = JSONWriter.of(); + jsonWriter.write((JSONObject) null); + assertEquals("null", jsonWriter.toString()); + } + { + JSONWriter jsonWriter = JSONWriter.ofUTF8(); + jsonWriter.write((JSONObject) null); + assertEquals("null", jsonWriter.toString()); + } + { + JSONWriter jsonWriter = JSONWriter.ofUTF16(); + jsonWriter.write((JSONObject) null); + assertEquals("null", jsonWriter.toString()); + } + { + JSONWriter jsonWriter = JSONWriter.ofPretty(); + jsonWriter.write((JSONObject) null); + assertEquals("null", jsonWriter.toString()); + } + { + JSONWriter jsonWriter = JSONWriter.ofPretty(); + jsonWriter.write((Map) null); + assertEquals("null", jsonWriter.toString()); + } + } } diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java index d9d8f8216..389776a07 100644 --- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java +++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java @@ -644,7 +644,7 @@ public void writeName8() { @Test public void writeName8Pretty() { JSONWriterUTF16 jsonWriter = new JSONWriterUTF16(JSONFactory.createWriteContext(PrettyFormat)); - jsonWriter.indent += 2; + jsonWriter.level += 2; char[] name = "a123".toCharArray(); long nameValue = UNSAFE.getLong(name, ARRAY_CHAR_BASE_OFFSET); @@ -712,13 +712,14 @@ public void issue_2484() { char[] name = "a123".toCharArray(); long nameValue = UNSAFE.getLong(name, ARRAY_CHAR_BASE_OFFSET); + byte PRETTY_NON = 0, PRETTY_TAB = 1, PRETTY_SPACE = 3; final int initOffset = 8183; for (int i = 0; i < 16; i++) { int initSize = 8196 - i; { jsonWriter.startObject = true; - jsonWriter.pretty = false; + jsonWriter.pretty = PRETTY_NON; jsonWriter.chars = new char[initSize]; jsonWriter.off = initOffset; @@ -726,7 +727,7 @@ public void issue_2484() { } { jsonWriter.startObject = false; - jsonWriter.pretty = false; + jsonWriter.pretty = PRETTY_NON; jsonWriter.chars = new char[initSize]; jsonWriter.off = initOffset; @@ -734,7 +735,7 @@ public void issue_2484() { } { jsonWriter.startObject = false; - jsonWriter.pretty = true; + jsonWriter.pretty = PRETTY_TAB; jsonWriter.chars = new char[initSize]; jsonWriter.off = initOffset; diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java index 8c37f02a4..cda2c0150 100644 --- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java +++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java @@ -1,5 +1,6 @@ package com.alibaba.fastjson2; +import com.alibaba.fastjson2.JSONWriter.Context; import com.alibaba.fastjson2.annotation.JSONField; import org.junit.jupiter.api.Test; @@ -672,6 +673,20 @@ public void writeStringLatin1Pretty() { assertEquals(str, parse); } + @Test + public void writeStringEscaped() { + byte[] bytes = new byte[1024 * 128]; + Arrays.fill(bytes, (byte) 1); + Context context = JSONFactory.createWriteContext(JSONWriter.Feature.PrettyFormat); + try (JSONWriterUTF8 jsonWriter = new JSONWriterUTF8(context)) { + jsonWriter.writeStringEscaped(bytes); + String json = jsonWriter.toString(); + String str = new String(bytes, 0, bytes.length, StandardCharsets.ISO_8859_1); + Object parse = JSON.parse(json); + assertEquals(str, parse); + } + } + @Test public void writeName8() { JSONWriterUTF8 jsonWriter = new JSONWriterUTF8(JSONFactory.createWriteContext()); diff --git a/core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest4.java b/core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest4.java index d8b9b333e..bc4a7012a 100644 --- a/core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest4.java +++ b/core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest4.java @@ -221,8 +221,8 @@ public void test6() { byte[] jsonbBytes = JSONB.toBytes(ex, writerFeatures); UncheckedIOException ex1 = (UncheckedIOException) JSONB.parseObject(jsonbBytes, Object.class, readerFeatures); - assertEquals(ex.getMessage(), ex1.getMessage()); - assertEquals(ex.getCause().getMessage(), ex1.getCause().getMessage()); + assertNull(ex.getMessage(), "Expected original message to be null"); + assertEquals(ex.getCause() != null ? ex.getCause().getMessage() : null, ex1.getCause() != null ? ex1.getCause().getMessage() : null); } @Test diff --git a/core/src/test/java/com/alibaba/fastjson2/features/PrettyFormatTest.java b/core/src/test/java/com/alibaba/fastjson2/features/PrettyFormatTest.java new file mode 100644 index 000000000..a8d016aef --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/features/PrettyFormatTest.java @@ -0,0 +1,313 @@ +package com.alibaba.fastjson2.features; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; +import lombok.Data; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import static com.alibaba.fastjson2.JSONWriter.Feature.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PrettyFormatTest { + @Test + public void test() { + assertEquals( + "{\n" + + "\t\"id\":123,\n" + + "\t\"value\":\"abc\"\n" + + "}", + JSONObject.of("id", 123, "value", "abc") + .toString(PrettyFormat)); + assertEquals( + "{\n" + + "\t\"id\":123,\n" + + "\t\"value\":\"abc\"\n" + + "}", + JSONObject.of("id", 123, "value", "abc") + .toString(PrettyFormat, OptimizedForAscii)); + assertEquals( + "{\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + "}", + JSONObject.of("id", 123, "value", "abc") + .toString(PrettyFormatWith4Space)); + assertEquals( + "{\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + "}", + JSONObject.of("id", 123, "value", "abc") + .toString(PrettyFormatWith4Space, OptimizedForAscii)); + + assertEquals( + "{\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + "}", + JSONObject.of("id", 123, "value", "abc") + .toString(PrettyFormatWith2Space)); + assertEquals( + "{\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + "}", + JSONObject.of("id", 123, "value", "abc") + .toString(PrettyFormatWith2Space, OptimizedForAscii)); + } + + @Test + public void test1() { + Bean1 bean = new Bean1(); + bean.setId(123); + bean.setValue("abc"); + + assertEquals( + "{\n" + + "\t\"id\":123,\n" + + "\t\"value\":\"abc\"\n" + + "}", + JSON.toJSONString(bean, PrettyFormat)); + assertEquals( + "{\n" + + "\t\"id\":123,\n" + + "\t\"value\":\"abc\"\n" + + "}", + JSON.toJSONString(bean, PrettyFormat, OptimizedForAscii)); + assertEquals( + "{\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + "}", + JSON.toJSONString(bean, PrettyFormatWith4Space)); + assertEquals( + "{\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + "}", + JSON.toJSONString(bean, PrettyFormatWith4Space, OptimizedForAscii)); + + Bean1[] array = new Bean1[] {bean}; + assertEquals( + "[\n" + + "\t{\n" + + "\t\t\"id\":123,\n" + + "\t\t\"value\":\"abc\"\n" + + "\t}\n" + + "]", + JSON.toJSONString(array, PrettyFormat)); + assertEquals( + "[\n" + + " {\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + " }\n" + + "]", + JSON.toJSONString(array, PrettyFormatWith4Space)); + } + + @Data + private static class Bean1 { + private int id; + private String value; + } + + @Test + public void test2() { + Bean2 bean = new Bean2(); + bean.setId(123); + bean.setValue("abc"); + + assertEquals( + "{\n" + + "\t\"id\":123,\n" + + "\t\"value\":\"abc\"\n" + + "}", + JSON.toJSONString(bean, PrettyFormat)); + assertEquals( + "{\n" + + "\t\"id\":123,\n" + + "\t\"value\":\"abc\"\n" + + "}", + JSON.toJSONString(bean, PrettyFormat, OptimizedForAscii)); + assertEquals( + "{\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + "}", + JSON.toJSONString(bean, PrettyFormatWith4Space)); + assertEquals( + "{\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + "}", + JSON.toJSONString(bean, PrettyFormatWith4Space, OptimizedForAscii)); + + Bean2[] array = new Bean2[] {bean}; + assertEquals( + "[\n" + + "\t{\n" + + "\t\t\"id\":123,\n" + + "\t\t\"value\":\"abc\"\n" + + "\t}\n" + + "]", + JSON.toJSONString(array, PrettyFormat)); + assertEquals( + "[\n" + + " {\n" + + " \"id\":123,\n" + + " \"value\":\"abc\"\n" + + " }\n" + + "]", + JSON.toJSONString(array, PrettyFormatWith4Space)); + } + + @Data + public static class Bean2 { + private int id; + private String value; + } + + @Test + public void test3() { + Bean3 bean = new Bean3(); + bean.values.add("a01"); + bean.values.add("a02"); + + assertEquals( + "{\n" + + "\t\"values\":[\n" + + "\t\t\"a01\",\n" + + "\t\t\"a02\"\n" + + "\t]\n" + + "}", + JSON.toJSONString(bean, PrettyFormat)); + assertEquals( + "{\n" + + "\t\"values\":[\n" + + "\t\t\"a01\",\n" + + "\t\t\"a02\"\n" + + "\t]\n" + + "}", + JSON.toJSONString(bean, PrettyFormat, OptimizedForAscii)); + } + + public static class Bean3 { + public List values = new ArrayList<>(); + } + + @Test + public void ofPrettyUTF16() { + JSONWriter jsonWriter = JSONWriter.ofUTF16(); + jsonWriter = JSONWriter.ofPretty(jsonWriter); + jsonWriter = JSONWriter.ofPretty(jsonWriter); + jsonWriter.startObject(); + jsonWriter.writeNameValue("id", 123); + jsonWriter.endObject(); + jsonWriter.close(); + + assertEquals("{\n" + + "\t\"id\"123\n" + + "}", jsonWriter.toString()); + + jsonWriter.incrementIndent(); + assertEquals(1, jsonWriter.level()); + jsonWriter.println(); + assertEquals("{\n" + + "\t\"id\"123\n" + + "}\n\t", jsonWriter.toString()); + jsonWriter.decrementIdent(); + assertEquals(0, jsonWriter.level()); + } + + @Test + public void ofPrettyUTF8() { + JSONWriter jsonWriter = JSONWriter.ofUTF8(); + jsonWriter = JSONWriter.ofPretty(jsonWriter); + jsonWriter = JSONWriter.ofPretty(jsonWriter); + jsonWriter.startObject(); + jsonWriter.writeNameValue("id", 123); + jsonWriter.endObject(); + jsonWriter.close(); + + assertEquals("{\n" + + "\t\"id\"123\n" + + "}", jsonWriter.toString()); + + jsonWriter.incrementIndent(); + assertEquals(1, jsonWriter.level()); + jsonWriter.println(); + assertEquals("{\n" + + "\t\"id\"123\n" + + "}\n\t", jsonWriter.toString()); + jsonWriter.decrementIdent(); + assertEquals(0, jsonWriter.level()); + } + + @Test + public void testJSONObject() { + { + JSONObject object = JSONObject.of( + "f0", null, + "f1", 101, + "f2", 102L, + "f3", new BigDecimal("103"), + "f4", new JSONObject(), + "f5", new JSONArray(), + "f6", (short) 106, + "f7", true + ); + { + JSONWriter jsonWriter = JSONWriter.ofUTF16(PrettyFormat); + jsonWriter.write(object); + assertEquals("{\n" + + "\t\"f1\":101,\n" + + "\t\"f2\":102,\n" + + "\t\"f3\":103,\n" + + "\t\"f4\":{},\n" + + "\t\"f5\":[],\n" + + "\t\"f6\":106,\n" + + "\t\"f7\":true\n" + + "}", jsonWriter.toString()); + } + { + JSONWriter jsonWriter = JSONWriter.ofUTF8(PrettyFormat); + jsonWriter.write(object); + assertEquals("{\n" + + "\t\"f1\":101,\n" + + "\t\"f2\":102,\n" + + "\t\"f3\":103,\n" + + "\t\"f4\":{},\n" + + "\t\"f5\":[],\n" + + "\t\"f6\":106,\n" + + "\t\"f7\":true\n" + + "}", jsonWriter.toString()); + } + { + JSONWriter jsonWriter = JSONWriter.of(PrettyFormat, WriteNulls); + jsonWriter.write(object); + assertEquals("{\n" + + "\t\"f0\":null,\n" + + "\t\"f1\":101,\n" + + "\t\"f2\":102,\n" + + "\t\"f3\":103,\n" + + "\t\"f4\":{},\n" + + "\t\"f5\":[],\n" + + "\t\"f6\":106,\n" + + "\t\"f7\":true\n" + + "}", jsonWriter.toString()); + } + } + { + JSONWriter jsonWriter = JSONWriter.of(PrettyFormat); + jsonWriter.write(JSONObject.of()); + assertEquals("{}", jsonWriter.toString()); + } + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_1500/Issue1591.java b/core/src/test/java/com/alibaba/fastjson2/issues_1500/Issue1591.java index a8e11e3f8..e46b4e3a4 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues_1500/Issue1591.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues_1500/Issue1591.java @@ -25,9 +25,16 @@ public void test() { ); } - String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat); - assertNotNull(str); - assertEquals(array, JSON.parseArray(str)); + { + String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat); + assertNotNull(str); + assertEquals(array, JSON.parseArray(str)); + } + { + String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat, JSONWriter.Feature.OptimizedForAscii); + assertNotNull(str); + assertEquals(array, JSON.parseArray(str)); + } } @Test @@ -49,8 +56,15 @@ public void test1() { ); } - String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat); - assertNotNull(str); - assertEquals(array, JSON.parseArray(str)); + { + String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat); + assertNotNull(str); + assertEquals(array, JSON.parseArray(str)); + } + { + String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat, JSONWriter.Feature.OptimizedForAscii); + assertNotNull(str); + assertEquals(array, JSON.parseArray(str)); + } } } diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_2900/Issue2959.java b/core/src/test/java/com/alibaba/fastjson2/issues_2900/Issue2959.java index 58b6c05fd..45601d7db 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues_2900/Issue2959.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues_2900/Issue2959.java @@ -8,14 +8,21 @@ import java.math.BigDecimal; import java.util.Date; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; public class Issue2959 { @Test public void test() { - assertEquals("{\"projectNO\":null,\"orderNO\":null,\"orderType\":null,\"pN\":null,\"qty\":null,\"description\":null,\"customerCode\":null,\"customerName\":null,\"currency\":null,\"netPrice\":null,\"uSD\":null,\"rate\":null,\"orderDate\":null,\"requestDate\":null,\"planedDate\":null,\"this$0\":null}", - JSON.toJSONString(new TobeDelivery(), JSONWriter.Feature.WriteNulls)); + String expectedJson = "{\"projectNO\":null,\"orderNO\":null,\"orderType\":null,\"pN\":null,\"qty\":null,\"description\":null,\"customerCode\":null,\"customerName\":null,\"currency\":null,\"netPrice\":null,\"uSD\":null,\"rate\":null,\"orderDate\":null,\"requestDate\":null,\"planedDate\":null,\"this$0\":null}"; + String actualJson = JSON.toJSONString(new TobeDelivery(), JSONWriter.Feature.WriteNulls); + + // Parse the JSON strings into Maps for comparison + Map expectedMap = JSON.parseObject(expectedJson, Map.class); + Map actualMap = JSON.parseObject(actualJson, Map.class); + + assertEquals(expectedMap, actualMap); } @Data diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_2900/Issue2981.java b/core/src/test/java/com/alibaba/fastjson2/issues_2900/Issue2981.java new file mode 100644 index 000000000..3bd84ceb8 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_2900/Issue2981.java @@ -0,0 +1,28 @@ +package com.alibaba.fastjson2.issues_2900; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONType; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Issue2981 { + @Test + public void test() { + Shape.Circle circle = new Shape.Circle(); + circle.setRadius(5); + assertEquals(JSON.parse(JSON.toJSONString(circle)), JSON.toJSON(circle)); + } + + @JSONType(typeKey = "type", seeAlso = Shape.Circle.class) + public static class Shape { + @JSONType(typeKey = "type", typeName = "circle", serializeFeatures = JSONWriter.Feature.WriteClassName) + public static class Circle + extends Shape { + private int radius; + public int getRadius() { return radius; } + public void setRadius(int radius) { this.radius = radius; } + } + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3125.java b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3125.java new file mode 100644 index 000000000..1b3239377 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3125.java @@ -0,0 +1,50 @@ +package com.alibaba.fastjson2.issues_3100; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONPath; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Issue3125 { + @Getter + @Setter + static class User { + private String name; + private List additionalInfo; + } + + @Getter + @Setter + @ToString + static class NVString { + String name; + String value; + } + + public static final String str = "{\"name\":\"fastjson2\",\"additionalInfo\":[{\"name\":\"srv6-color\",\"value\":\"test\"},{\"name\":\"test2\",\"value\":\"3\"}]}"; + + public static String path = "$.additionalInfo[?(@.name=='srv6-color')].value"; + + @Test + public void test_fastjson2_case1() { + User user = JSON.parseObject(str, User.class); + JSONPath jsonPath = JSONPath.of("$.additionalInfo[?(@.name=='srv6-color')].value"); + jsonPath.set(user, "modify"); + assertEquals(user.getAdditionalInfo().get(0).getValue(), "modify"); + } + + @Test + public void test_fastjson_case1() { + User user = JSON.parseObject(str, User.class); + com.alibaba.fastjson.JSONPath.set(user, "$.additionalInfo[?(@.name=='srv6-color')].value", "modify"); + JSONPath jsonPath = JSONPath.of("$.additionalInfo[?(@.name=='srv6-color')].value"); + jsonPath.set(user, "modify"); + assertEquals(user.getAdditionalInfo().get(0).getValue(), "modify"); + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3134.java b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3134.java new file mode 100644 index 000000000..dc44ce507 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3134.java @@ -0,0 +1,37 @@ +package com.alibaba.fastjson2.issues_3100; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import lombok.Data; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Issue3134 { + @Test + public void test() { + String json = "{\"name\":\"希望小学\",\"students\":{\"age\":\"12\",\"id\":\"1\",\"name\":\"张宇\"}}"; + School schoolOne = JSON.parseObject(json, School.class); + JSONObject schoolTwo = JSON.parseObject(json); + School schoolThree = schoolTwo.toJavaObject(School.class); + JSONObject studentOne = schoolTwo.getJSONObject("students"); + JSONArray studentTwo = schoolTwo.getJSONArray("students"); + assertEquals("[{\"age\":\"12\",\"id\":\"1\",\"name\":\"张宇\"}]", studentTwo.toString()); + } + + @Data + private static class School{ + private String name; + private List students; + } + + @Data + private static class Student{ + private String name; + private String id; + private String age; + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3139.kt b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3139.kt new file mode 100644 index 000000000..5de2e1849 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3139.kt @@ -0,0 +1,20 @@ +package com.alibaba.fastjson2.issues_3100 + +import com.alibaba.fastjson2.JSON +import com.alibaba.fastjson2.JSONB +import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals + +class Issue3139 { + data class Item(val value: String = "默认值") + data class Result(val low: Item = Item(), val mid: Item = Item(), val high: Item = Item() ) + + @Test + fun test() { + var r = Result(Item("L"), Item("M"), Item("H")) + var jsonb = JSONB.toBytes(r) + println(JSONB.toJSONString(jsonb)) + var r1 = JSONB.parseObject(jsonb, Result::class.java) + assertEquals("{\"high\":{\"value\":\"H\"},\"low\":{\"value\":\"L\"},\"mid\":{\"value\":\"M\"}}", JSON.toJSONString(r1)) + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3152.kt b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3152.kt new file mode 100644 index 000000000..4463eeac1 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3152.kt @@ -0,0 +1,28 @@ +package com.alibaba.fastjson2.issues_3100 + +import com.alibaba.fastjson2.JSON +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + + +class Issue3152 { + + data class Foo(val int: Int = 1, val bar: Bar) + data class Bar(val text: String) + + @Test + fun test() { + Assertions.assertEquals( + Foo(1, Bar("hello")), + JSON.parseObject( + """ + { + "bar": { + "text" : "hello" + } + } + """.trimIndent() + ).toJavaObject(Foo::class.java) + ) + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3157Test.java b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3157Test.java new file mode 100644 index 000000000..292772d0e --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3157Test.java @@ -0,0 +1,54 @@ +package com.alibaba.fastjson2.issues_3100; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializeConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.serializer.ToStringSerializer; +import org.junit.Assert; +import org.junit.jupiter.api.Test; + +/** + * Issue3157测试类 + * @author changyi.ccy + */ +public class Issue3157Test { + /** + * 测试: Issue3157 + */ + @Test + public void testIssue3157() { + SerializeConfig stringSerializeConfig = new SerializeConfig(); + stringSerializeConfig.put(Integer.class, ToStringSerializer.instance); + stringSerializeConfig.put(Long.class, ToStringSerializer.instance); + People people = new People("Test", 10, 10000000L); + String jsonText = "{\"age\":\"10\",\"hairNums\":\"10000000\",\"name\":\"Test\"}"; + Assert.assertEquals("JSON文本不一致", jsonText, JSON.toJSONString(people, stringSerializeConfig, new SerializerFeature[0])); + } + + /** + * People类 + */ + public static class People { + private String name; + private Integer age; + private Long hairNums; + + public People(String name, Integer age, Long hairNums) { + this.name = name; + this.age = age; + this.hairNums = hairNums; + } + + public String getName() { + return name; + } + + public Integer getAge() { + return age; + } + + public Long getHairNums() { + return hairNums; + } + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3163.java b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3163.java new file mode 100644 index 000000000..31c461616 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3163.java @@ -0,0 +1,20 @@ +package com.alibaba.fastjson2.issues_3100; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.annotation.JSONField; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Issue3163 { + @Test + public void test() { + Bean bean = JSON.parseObject("{\"id\":1234}", Bean.class); + assertEquals(0, bean.id); + } + + public static class Bean { + @JSONField(deserialize = false) + public int id; + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3170Test.java b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3170Test.java new file mode 100644 index 000000000..4c5dfb193 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3170Test.java @@ -0,0 +1,108 @@ +package com.alibaba.fastjson2.issues_3100; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.serializer.SerializeConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.serializer.ToStringSerializer; +import com.alibaba.fastjson.serializer.ValueFilter; +import org.junit.Assert; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * Issue3170测试类 + * @author changyi.ccy + */ +public class Issue3170Test { + /** + * 测试: Issue3170 + */ + @Test + public void testIssue3170() { + IntegerToStringFilter integerToStringFilter = new IntegerToStringFilter(); + SerializeConfig stringSerializeConfig = new SerializeConfig(); + stringSerializeConfig.put(Integer.class, ToStringSerializer.instance); + stringSerializeConfig.put(Long.class, ToStringSerializer.instance); + + People one = new People(10, "aaa", 10000000L); + People two = new People(40, "bbb", 0L); + two.setSon(one); + List peopleList = new ArrayList<>(); + peopleList.add(one); + peopleList.add(two); + String jsonText = "[{\"age\":\"10\",\"hairNums\":\"10000000\",\"name\":\"aaa\"}," + + "{\"age\":\"40\",\"hairNums\":\"0\",\"name\":\"bbb\",\"son\":" + + "{\"age\":\"10\",\"hairNums\":\"10000000\",\"name\":\"aaa\"}}]"; + Assert.assertEquals("JSON文本不一致", jsonText, JSON.toJSONString(peopleList, + stringSerializeConfig, integerToStringFilter)); + } + + /** + * People类 + */ + public static class People { + private Long hairNums; + private String name; + private Integer age; + @JSONField(serialzeFeatures = SerializerFeature.DisableCircularReferenceDetect) + private People son; + @JSONField(format = "yyyy-MM-dd") + private Date applyStartTime; + + public People(Integer age, String name, Long hairNums) { + this.age = age; + this.name = name; + this.hairNums = hairNums; + } + + public Integer getAge() { + return age; + } + + public String getName() { + return name; + } + + public Long getHairNums() { + return hairNums; + } + + public People getSon() { + return son; + } + + public void setSon(People son) { + this.son = son; + } + + public Date getApplyStartTime() { + return applyStartTime; + } + + public void setApplyStartTime(Date applyStartTime) { + this.applyStartTime = applyStartTime; + } + } + + /** + * 整数转字符串过滤器类 + */ + public static class IntegerToStringFilter + implements ValueFilter { + @Override + public Object process(Object o, String s, Object v) { + if (Objects.isNull(v)) { + return null; + } + if (v instanceof Integer) { + return v.toString(); + } + return v; + } + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3172Kt.kt b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3172Kt.kt new file mode 100644 index 000000000..c36cdee44 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3172Kt.kt @@ -0,0 +1,21 @@ +package com.alibaba.fastjson2.issues_3100 + +import com.alibaba.fastjson2.JSON +import com.fasterxml.jackson.annotation.JsonValue +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +enum class UserStatus(@JsonValue val value: Int, val label: String) { + NORMAL(1, "正常"), + LOCKED(2, "锁定"), +} + +class EnumTest { + @Test + fun testEnum() { + val value = "\"2\"" + val status = JSON.parseObject(value, UserStatus::class.java) + println("Enum Item: $status") // null, version=2.0.53 + Assertions.assertEquals(UserStatus.LOCKED, status) + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3192.java b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3192.java new file mode 100644 index 000000000..078f894b9 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3192.java @@ -0,0 +1,20 @@ +package com.alibaba.fastjson2.issues_3100; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSONPath; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Issue3192 { + @Test + public void test_jsonpath_array() { + String json = "{ \"array\": null }"; + assertEquals("[]", JSON.toJSONString(JSONPath.extract(json, "$.array[0].key", JSONPath.Feature.KeepNullValue, JSONPath.Feature.AlwaysReturnList))); + assertEquals("[]", JSON.toJSONString(JSONPath.extract(json, "$.array[*].key", JSONPath.Feature.KeepNullValue, JSONPath.Feature.AlwaysReturnList))); + + json = "{ \"array\": [{\"key\":1}, {\"key\":null}, {\"key\":3}] }"; + assertEquals("[1]", JSON.toJSONString(JSONPath.extract(json, "$.array[0].key", JSONPath.Feature.KeepNullValue, JSONPath.Feature.AlwaysReturnList))); + assertEquals("[1,null,3]", JSON.toJSONString(JSONPath.extract(json, "$.array[*].key", JSONPath.Feature.KeepNullValue, JSONPath.Feature.AlwaysReturnList))); + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3208.java b/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3208.java new file mode 100644 index 000000000..4d6ad314c --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3208.java @@ -0,0 +1,27 @@ +package com.alibaba.fastjson2.issues_3200; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializeConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +public class Issue3208 { + @Test + public void testArray() { + byte[] bytes = new byte[]{1, 2, 3}; + String jsonString = JSON.toJSONString(bytes, SerializerFeature.WriteClassName); + byte[] bytes1 = (byte[]) JSON.parse(jsonString); + Assertions.assertTrue(Arrays.equals(bytes, bytes1)); + } + + @Test + public void testObjectArray() { + byte[] jsonBytes = JSON.toJSONBytes(new Object[]{JSON.toJSONString(ImmutableMap.of("a", 1))}, new SerializeConfig(true), JSON.DEFAULT_GENERATE_FEATURE, SerializerFeature.WriteClassName); + Object parsed = JSON.parse(new String(jsonBytes)); + Assertions.assertNotNull(parsed); + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3210.java b/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3210.java new file mode 100644 index 000000000..c5b74798a --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3210.java @@ -0,0 +1,23 @@ +package com.alibaba.fastjson2.issues_3200; + +import com.alibaba.fastjson2.JSONObject; +import lombok.Data; +import org.junit.jupiter.api.Test; + +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class Issue3210 { + @Data + public static class Bean { + private Date time; + } + + @Test + public void test() { + String s = "{\"time\":\"2024-12-04T20:43:15.000000999\"}"; + Bean bean = JSONObject.parseObject(s, Bean.class); + assertNotNull(bean.time); + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3220.java b/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3220.java new file mode 100644 index 000000000..c88b9b2e4 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3200/Issue3220.java @@ -0,0 +1,27 @@ +package com.alibaba.fastjson2.issues_3200; + +import com.alibaba.fastjson2.JSON; +import lombok.Data; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Issue3220 { + @Test + public void test() { + Param param = new Param(); + param.setUserName("张三"); + param.setCode("001"); + param.setSort("1"); + param.setStatus("正常"); + assertEquals("{\"code\":\"001\",\"sort\":\"1\",\"status\":\"正常\",\"userName\":\"张三\"}", JSON.toJSONString(param)); + } + + @Data + private static class Param { + public String UserName; + String Code; + String Sort; + String Status; + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/support/csv/CSVWriterTest.java b/core/src/test/java/com/alibaba/fastjson2/support/csv/CSVWriterTest.java index 21db7bbe2..d051cc048 100644 --- a/core/src/test/java/com/alibaba/fastjson2/support/csv/CSVWriterTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/support/csv/CSVWriterTest.java @@ -3,11 +3,10 @@ import com.alibaba.fastjson2.reader.ByteArrayValueConsumer; import com.alibaba.fastjson2.reader.CharArrayValueConsumer; import com.alibaba.fastjson2.util.DateUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.StringWriter; +import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; @@ -334,6 +333,22 @@ public void test6UTF8() throws Exception { } } + @Test + public void test7() throws Exception { + try (CSVWriter csvWriter = CSVWriter.of(new StringWriter())) { + csvWriter.writeString(",abc".getBytes()); + assertEquals("\",abc\"", csvWriter.toString()); + } + } + + @Test + public void test7UTF8() throws Exception { + try (CSVWriter csvWriter = CSVWriter.of(new ByteArrayOutputStream())) { + csvWriter.writeString(",abc".getBytes()); + assertEquals("\",abc\"", csvWriter.toString()); + } + } + @Test public void testWriteInstant() throws Exception { LocalDate date = LocalDate.of(2018, 7, 12); @@ -387,6 +402,36 @@ public void testWriteInstant1UTF8() throws Exception { } } + @Test + public void testWriteStringForIssue2848() throws IOException { + // see issue#2848 + OutputStreamWriter out = new OutputStreamWriter(new ByteArrayOutputStream(), StandardCharsets.UTF_8); + try (CSVWriter writer = CSVWriter.of(out)) { + writer.writeValue(StringUtils.repeat('1', 65534)); + writer.writeComma(); + writer.writeString("123"); // java.lang.StringIndexOutOfBoundsException: offset 65535, count 3, length 65536 + } + } + + @Test + public void testWriteLocalDateTimeForIssue2848() throws IOException { + OutputStreamWriter out = new OutputStreamWriter(new ByteArrayOutputStream(), StandardCharsets.UTF_8); + try (CSVWriter writer = CSVWriter.of(out)) { + writer.writeValue(StringUtils.repeat('1', 65534)); + writer.writeComma(); + writer.writeLocalDateTime(LocalDateTime.now()); // java.lang.ArrayIndexOutOfBoundsException: Index 65539 out of bounds for length 65536 + } + } + + @Test + public void testWriteLargeStringForIssue2848() throws IOException { + OutputStreamWriter out = new OutputStreamWriter(new ByteArrayOutputStream(), StandardCharsets.UTF_8); + try (CSVWriter writer = CSVWriter.of(out)) { + writer.writeValue(StringUtils.repeat('1', 65534)); + writer.writeComma(); + writer.writeString(StringUtils.repeat('1', 65537)); // java.lang.StringIndexOutOfBoundsException: offset 65535, count 65537, length 65536 + } + } public static class Bean { public int id; public String name; diff --git a/core/src/test/java/com/alibaba/fastjson2/write/PrettyTest.java b/core/src/test/java/com/alibaba/fastjson2/write/PrettyTest.java index e47e9f00a..462a7e46b 100644 --- a/core/src/test/java/com/alibaba/fastjson2/write/PrettyTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/write/PrettyTest.java @@ -182,9 +182,7 @@ public void test_pretty_object_empty() { JSONWriter jw = JSONWriter.ofPretty(); jw.writeAny(Collections.emptyMap()); - assertEquals("{\n" + - "\t\n" + - "}", + assertEquals("{}", jw.toString()); } @@ -201,12 +199,8 @@ public void test_pretty_map_2() { assertEquals("{\n" + "\t\"id\":1001,\n" + "\t\"name\":\"DataWorks\",\n" + - "\t\"array\":[\n" + - "\t\t\n" + - "\t],\n" + - "\t\"object\":{\n" + - "\t\t\n" + - "\t}\n" + + "\t\"array\":[],\n" + + "\t\"object\":{}\n" + "}", jw.toString()); } @@ -215,7 +209,7 @@ public void test_pretty_map_2() { public void test_pretty_array() { JSONWriter jw = JSONWriter.ofPretty(); jw.writeAny(Collections.emptyList()); - assertEquals("[\n\t\n]", jw.toString()); + assertEquals("[]", jw.toString()); } @Test diff --git a/docs/index.md b/docs/index.md index c17a03d8b..aecf3838f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -194,8 +194,8 @@ byte[] text = JSON.toJSONBytes(data); import com.alibaba.fastjson2.* val data = ... // Any -val text = text.toJSONString() // String -val bytes = text.toJSONByteArray() // ByteArray +val text = data.toJSONString() // String +val bytes = data.toJSONByteArray() // ByteArray ``` ### 2.5 使用`JSONObject`、`JSONArray` diff --git a/docs/kotlin_cn.md b/docs/kotlin_cn.md index 50375f5f3..7c00edfc5 100644 --- a/docs/kotlin_cn.md +++ b/docs/kotlin_cn.md @@ -127,14 +127,14 @@ val data = text.into>() // Map ```kotlin val data = "..." // Any -val text = text.toJSONString() // String +val text = data.toJSONString() // String ``` 序列化为字节数组: ```kotlin val data = "..." // Any -val bytes = text.toJSONByteArray() // ByteArray +val bytes = data.toJSONByteArray() // ByteArray ``` ### 2.5 使用`JSONObject`、`JSONArray` diff --git a/docs/kotlin_en.md b/docs/kotlin_en.md index 172d31f48..fdda29e91 100644 --- a/docs/kotlin_en.md +++ b/docs/kotlin_en.md @@ -127,14 +127,14 @@ Serialization as a string: ```kotlin val data = "..." // Any -val text = text.toJSONString() // String +val text = data.toJSONString() // String ``` Serialization as a ByteArray: ```kotlin val data = "..." // Any -val bytes = text.toJSONByteArray() // ByteArray +val bytes = data.toJSONByteArray() // ByteArray ``` ### 2.5 Use `JSONObject`、`JSONArray` diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test0/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test0/QuickConfigTest.java index 0acbf332a..5a7501b60 100644 --- a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test0/QuickConfigTest.java +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test0/QuickConfigTest.java @@ -10,7 +10,7 @@ import org.noear.solon.test.SolonTest; import java.util.Date; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -26,7 +26,7 @@ public class QuickConfigTest { public void hello2() throws Throwable { UserDo userDo = new UserDo(); - Map data = new HashMap<>(); + Map data = new LinkedHashMap<>(); data.put("time", new Date(1673861993477L)); data.put("long", 12L); data.put("int", 12); diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest.java index 67c04db8c..1254f93dd 100644 --- a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest.java +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest.java @@ -10,7 +10,7 @@ import org.noear.solon.test.SolonTest; import java.util.Date; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -26,7 +26,7 @@ public class QuickConfigTest { public void hello2() throws Throwable { UserDo userDo = new UserDo(); - Map data = new HashMap<>(); + Map data = new LinkedHashMap<>(); data.put("time", new Date(1673861993477L)); data.put("long", 12L); data.put("int", 12); diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest2.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest2.java index 2b878c2b2..710a38a88 100644 --- a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest2.java +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest2.java @@ -10,7 +10,7 @@ import org.noear.solon.test.SolonTest; import java.util.Date; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -26,7 +26,7 @@ public class QuickConfigTest2 { public void hello2() throws Throwable { UserDo userDo = new UserDo(); - Map data = new HashMap<>(); + Map data = new LinkedHashMap<>(); data.put("time", new Date(1673861993477L)); data.put("long", 12L); data.put("int", 12); diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test2/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test2/QuickConfigTest.java index 7c28a3db6..b73fe1369 100644 --- a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test2/QuickConfigTest.java +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test2/QuickConfigTest.java @@ -11,7 +11,6 @@ import org.noear.solon.test.SolonTest; import java.util.Date; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -28,7 +27,7 @@ public class QuickConfigTest { public void hello2() throws Throwable { UserDo userDo = new UserDo(); - Map data = new HashMap<>(); + Map data = new LinkedHashMap<>(); data.put("time", new Date(1673861993477L)); data.put("long", 12L); data.put("int", 12); diff --git a/extension/pom.xml b/extension/pom.xml index 08add4ac9..308d5bf53 100644 --- a/extension/pom.xml +++ b/extension/pom.xml @@ -152,7 +152,7 @@ com.clickhouse clickhouse-jdbc - 0.6.5 + 0.7.1-patch1 http test diff --git a/mvnw b/mvnw index b7f064624..19529ddf8 100755 --- a/mvnw +++ b/mvnw @@ -19,269 +19,241 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.1.1 -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.2 # # Optional ENV vars # ----------------- -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME - else - JAVA_HOME="/Library/Java/Home"; export JAVA_HOME - fi - fi - ;; +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; esac -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" else JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi fi else - JAVACMD="`\\unset -f command; \\command -v java`" + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi fi -fi +} -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi +die() { + printf %s\\n "$1" >&2 + exit 1 +} - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - printf '%s' "$(cd "$basedir"; pwd)" +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' } -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" } -BASE_DIR=$(find_maven_basedir "$(dirname $0)") -if [ -z "$BASE_DIR" ]; then - exit 1; +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - else - wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $wrapperUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi + die "cannot create temp dir" +fi - if command -v wget > /dev/null; then - QUIET="--quiet" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - QUIET="" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" - else - wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" - fi - [ $? -eq 0 ] || rm -f "$wrapperJarPath" - elif command -v curl > /dev/null; then - QUIET="--silent" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - QUIET="" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L - else - curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L - fi - [ $? -eq 0 ] || rm -f "$wrapperJarPath" - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaSource=`cygpath --path --windows "$javaSource"` - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaSource" ]; then - if [ ! -e "$javaClass" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaSource") - fi - if [ -e "$javaClass" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" fi -########################################################################################## -# End of extension -########################################################################################## -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" fi -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 474c9d6b7..249bdf382 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,3 +1,4 @@ +<# : batch portion @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @@ -18,170 +19,131 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.1.1 -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir +@REM Apache Maven Wrapper startup batch script, version 3.3.2 @REM @REM Optional ENV vars -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output @REM ---------------------------------------------------------------------------- -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %WRAPPER_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) ) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index 01a9b2030..d65b6c103 100644 --- a/pom.xml +++ b/pom.xml @@ -663,7 +663,8 @@ com/alibaba/fastjson2/issues_1000/Issue1395.java, com/alibaba/fastjson2/issues_2100/Issue2164.java, com/alibaba/fastjson2/issues_2100/Issue2181.java, - com/alibaba/fastjson/v2issues/Issue1432.java + com/alibaba/fastjson/v2issues/Issue1432.java, + com/alibaba/fastjson2/issues_3200/Issue3220.java diff --git a/test-jdk17/src/test/java/com/alibaba/fastjson2/issues/Issue3090.java b/test-jdk17/src/test/java/com/alibaba/fastjson2/issues/Issue3090.java new file mode 100644 index 000000000..845e8675a --- /dev/null +++ b/test-jdk17/src/test/java/com/alibaba/fastjson2/issues/Issue3090.java @@ -0,0 +1,14 @@ +package com.alibaba.fastjson2.issues; + +import com.alibaba.fastjson2.JSON; +import org.junit.jupiter.api.Test; + +public class Issue3090 { + public record GoodUpdatedCheck(boolean isUpgrade, boolean isDowngrade, boolean isChangeSubscriptionGood) {} + @Test + public void test() { + GoodUpdatedCheck r = new GoodUpdatedCheck(true, false, false); + String str = JSON.toJSONString(r); + GoodUpdatedCheck r1 = JSON.parseObject(str, GoodUpdatedCheck.class); + } +} diff --git a/test-jdk17/src/test/java/com/alibaba/fastjson2/issues/Issue3104.java b/test-jdk17/src/test/java/com/alibaba/fastjson2/issues/Issue3104.java new file mode 100644 index 000000000..001b31b1d --- /dev/null +++ b/test-jdk17/src/test/java/com/alibaba/fastjson2/issues/Issue3104.java @@ -0,0 +1,31 @@ +package com.alibaba.fastjson2.issues; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.annotation.JSONCreator; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Issue3104 { + record Transform(Bridge bridge) { + public Transform(String from, String to) { + this(new Bridge(from, to)); + } + + @JSONCreator + public Transform(Bridge bridge) { + this.bridge = bridge; + } + } + + record Bridge(String from, String to) { + } + + @Test + public void test() { + String json = JSON.toJSONString(new Transform("zhangsan", "lisi")); + assertEquals("{\"bridge\":{\"from\":\"zhangsan\",\"to\":\"lisi\"}}", json); + Transform transform = JSON.parseObject(json, Transform.class); + assertEquals(json, JSON.toJSONString(transform)); + } +}