diff --git a/README.md b/README.md index a82fded8a..31e421816 100755 --- a/README.md +++ b/README.md @@ -1,25 +1,32 @@ # fastjson +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.alibaba/fastjson/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/fastjson/) +[![GitHub release](https://img.shields.io/github/release/alibaba/fastjson.svg)](https://github.com/alibaba/fastjson/releases) +[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) + Fast JSON Processor -![](logo.jpg) +![fastjson](logo.jpg "fastjson") ## Documentation -https://github.com/alibaba/fastjson/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98 + +- [Documentation Home](https://github.com/alibaba/fastjson/wiki) +- [Frequently Asked Questions](https://github.com/alibaba/fastjson/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98) ## Benchmark + https://github.com/eishay/jvm-serializers/wiki - ## Download -http://repo1.maven.org/maven2/com/alibaba/fastjson/ +http://repo1.maven.org/maven2/com/alibaba/fastjson/ ## Maven - - com.alibaba - fastjson - x.x.x - - +```xml + + com.alibaba + fastjson + x.x.x + +``` diff --git a/pom.xml b/pom.xml index d8c1831d7..2413cf03e 100755 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.alibaba fastjson - 1.2.5 + 1.2.6 jar fastjson diff --git a/src/main/java/com/alibaba/fastjson/JSON.java b/src/main/java/com/alibaba/fastjson/JSON.java index a46a8b3ee..7a57edb0d 100755 --- a/src/main/java/com/alibaba/fastjson/JSON.java +++ b/src/main/java/com/alibaba/fastjson/JSON.java @@ -737,5 +737,5 @@ public abstract class JSON implements JSONStreamAware, JSONAware { } } - public final static String VERSION = "1.2.5"; + public final static String VERSION = "1.2.6"; } diff --git a/src/main/java/com/alibaba/fastjson/annotation/JSONType.java b/src/main/java/com/alibaba/fastjson/annotation/JSONType.java index 6d0374072..f5ddd5d57 100755 --- a/src/main/java/com/alibaba/fastjson/annotation/JSONType.java +++ b/src/main/java/com/alibaba/fastjson/annotation/JSONType.java @@ -18,7 +18,9 @@ public @interface JSONType { boolean asm() default true; String[] orders() default {}; - + + String[] includes() default {}; + String[] ignores() default {}; SerializerFeature[] serialzeFeatures() default {}; diff --git a/src/main/java/com/alibaba/fastjson/serializer/MapSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/MapSerializer.java index e2b7b5555..681d6d004 100755 --- a/src/main/java/com/alibaba/fastjson/serializer/MapSerializer.java +++ b/src/main/java/com/alibaba/fastjson/serializer/MapSerializer.java @@ -160,7 +160,8 @@ public class MapSerializer implements ObjectSerializer { } if (out.isEnabled(SerializerFeature.BrowserCompatible) - || out.isEnabled(SerializerFeature.WriteNonStringKeyAsString)) { + || out.isEnabled(SerializerFeature.WriteNonStringKeyAsString) + || out.isEnabled(SerializerFeature.BrowserSecure)) { String strEntryKey = JSON.toJSONString(entryKey); serializer.write(strEntryKey); } else { diff --git a/src/main/java/com/alibaba/fastjson/serializer/SerializeWriter.java b/src/main/java/com/alibaba/fastjson/serializer/SerializeWriter.java index 7955660e0..080162bd8 100755 --- a/src/main/java/com/alibaba/fastjson/serializer/SerializeWriter.java +++ b/src/main/java/com/alibaba/fastjson/serializer/SerializeWriter.java @@ -642,7 +642,18 @@ public final class SerializeWriter extends Writer { for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); - if (isEnabled(SerializerFeature.BrowserCompatible)) { + if (isEnabled(SerializerFeature.BrowserSecure)) { + if (!(ch >= '0' && ch <= '9') && !(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') + && !(ch == ',') && !(ch == '.') && !(ch == '_')) { + write('\\'); + write('u'); + write(IOUtils.DIGITS[(ch >>> 12) & 15]); + write(IOUtils.DIGITS[(ch >>> 8) & 15]); + write(IOUtils.DIGITS[(ch >>> 4) & 15]); + write(IOUtils.DIGITS[ch & 15]); + continue; + } + } else if (isEnabled(SerializerFeature.BrowserCompatible)) { if (ch == '\b' // || ch == '\f' // || ch == '\n' // @@ -680,7 +691,15 @@ public final class SerializeWriter extends Writer { && IOUtils.specicalFlags_doubleQuotes[ch] != 0 // || (ch == '/' && isEnabled(SerializerFeature.WriteSlashAsSpecial))) { write('\\'); - write(replaceChars[(int) ch]); + if (IOUtils.specicalFlags_doubleQuotes[ch] == 4) { + write('u'); + write(IOUtils.DIGITS[ch >>> 12 & 15]); + write(IOUtils.DIGITS[ch >>> 8 & 15]); + write(IOUtils.DIGITS[ch >>> 4 & 15]); + write(IOUtils.DIGITS[ch & 15]); + } else { + write(IOUtils.replaceChars[ch]); + } continue; } } @@ -705,6 +724,51 @@ public final class SerializeWriter extends Writer { count = newcount; + if (isEnabled(SerializerFeature.BrowserSecure)) { + int lastSpecialIndex = -1; + + for (int i = start; i < end; ++i) { + char ch = buf[i]; + + if (!(ch >= '0' && ch <= '9') && !(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') + && !(ch == ',') && !(ch == '.') && !(ch == '_')) { + lastSpecialIndex = i; + newcount += 5; + continue; + } + } + + if (newcount > buf.length) { + expandCapacity(newcount); + } + count = newcount; + + for (int i = lastSpecialIndex; i >= start; --i) { + char ch = buf[i]; + + if (!(ch >= '0' && ch <= '9') && !(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') && !(ch == ',') + && !(ch == '.') && !(ch == '_')) { + System.arraycopy(buf, i + 1, buf, i + 6, end - i - 1); + buf[i] = '\\'; + buf[i + 1] = 'u'; + buf[i + 2] = IOUtils.DIGITS[(ch >>> 12) & 15]; + buf[i + 3] = IOUtils.DIGITS[(ch >>> 8) & 15]; + buf[i + 4] = IOUtils.DIGITS[(ch >>> 4) & 15]; + buf[i + 5] = IOUtils.DIGITS[ch & 15]; + end += 5; + } + } + + if (seperator != 0) { + buf[count - 2] = '\"'; + buf[count - 1] = seperator; + } else { + buf[count - 1] = '\"'; + } + + return; + } + if (isEnabled(SerializerFeature.BrowserCompatible)) { int lastSpecialIndex = -1; @@ -1192,7 +1256,11 @@ public final class SerializeWriter extends Writer { writeString(value); } } else { - if (isEnabled(SerializerFeature.BrowserCompatible)) { + if (isEnabled(SerializerFeature.BrowserSecure)) { + write(seperator); + writeStringWithDoubleQuote(name, ':'); + writeStringWithDoubleQuote(value, (char) 0); + } else if (isEnabled(SerializerFeature.BrowserCompatible)) { write(seperator); writeStringWithDoubleQuote(name, ':'); writeStringWithDoubleQuote(value, (char) 0); diff --git a/src/main/java/com/alibaba/fastjson/serializer/SerializerFeature.java b/src/main/java/com/alibaba/fastjson/serializer/SerializerFeature.java index dd5d67969..03bcf0b1b 100755 --- a/src/main/java/com/alibaba/fastjson/serializer/SerializerFeature.java +++ b/src/main/java/com/alibaba/fastjson/serializer/SerializerFeature.java @@ -121,7 +121,12 @@ public enum SerializerFeature { /** * @since 1.1.42 */ - NotWriteDefaultValue + NotWriteDefaultValue, + + /** + * @since 1.2.6 + */ + BrowserSecure, ; private SerializerFeature(){ diff --git a/src/main/java/com/alibaba/fastjson/util/TypeUtils.java b/src/main/java/com/alibaba/fastjson/util/TypeUtils.java index 0900749b0..bf1d9fdb6 100755 --- a/src/main/java/com/alibaba/fastjson/util/TypeUtils.java +++ b/src/main/java/com/alibaba/fastjson/util/TypeUtils.java @@ -1267,10 +1267,24 @@ public class TypeUtils { private static boolean isJSONTypeIgnore(Class clazz, String propertyName) { JSONType jsonType = clazz.getAnnotation(JSONType.class); - if (jsonType != null && jsonType.ignores() != null) { - for (String item : jsonType.ignores()) { - if (propertyName.equalsIgnoreCase(item)) { - return true; + if (jsonType != null) { + // 1、新增 includes 支持,如果 JSONType 同时设置了includes 和 ignores 属性,则以includes为准。 + // 2、个人认为对于大小写敏感的Java和JS而言,使用 equals() 比 equalsIgnoreCase() 更好,改动的唯一风险就是向后兼容性的问题 + // 不过,相信开发者应该都是严格按照大小写敏感的方式进行属性设置的 + String[] fields = jsonType.includes(); + if (fields.length > 0) { + for (int i = 0; i < fields.length; i++) { + if (propertyName.equals(fields[i])) { + return false; + } + } + return true; + } else { + fields = jsonType.ignores(); + for (int i = 0; i < fields.length; i++) { + if (propertyName.equals(fields[i])) { + return true; + } } } } diff --git a/src/test/java/com/alibaba/fastjson/serializer/SerializeWriterTest.java b/src/test/java/com/alibaba/fastjson/serializer/SerializeWriterTest.java new file mode 100644 index 000000000..05fe40334 --- /dev/null +++ b/src/test/java/com/alibaba/fastjson/serializer/SerializeWriterTest.java @@ -0,0 +1,83 @@ +package com.alibaba.fastjson.serializer; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.util.IOUtils; + +public class SerializeWriterTest { + + private final Logger logger = Logger.getLogger(SerializeWriterTest.class.getSimpleName()); + + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + private final SerializeWriter writer = new SerializeWriter(new OutputStreamWriter(baos)); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testWriteLiteBasicStr() throws UnsupportedEncodingException { + String targetStr = new String(IOUtils.DIGITS); + this.doTestWrite(targetStr); + } + + private String doTestWrite(String input) throws UnsupportedEncodingException { + writer.writeString(input, (char) 0); + writer.flush(); + String result = this.baos.toString("UTF-8"); + + Assert.assertEquals(input, JSON.parse(result)); + + logger.info(result); + + return result; + } + + @Test + public void testWriteLiteSpecilaStr() throws UnsupportedEncodingException { + this.doTestWrite(this.makeSpecialChars()); + } + + private String makeSpecialChars() { + StringBuilder strBuilder = new StringBuilder(128); + for (char c = 128; c <= 160; c++) { + strBuilder.append(c); + } + return strBuilder.toString(); + } + + @Test + public void testWriteLargeBasicStr() throws UnsupportedEncodingException { + String tmp = new String(IOUtils.DIGITS); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 200; i++) { + builder.append(tmp); + } + this.doTestWrite(builder.toString()); + } + + @Test + public void testWriteLargeSpecialStr() throws UnsupportedEncodingException { + + String tmp = this.makeSpecialChars(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 200; i++) { + builder.append(tmp); + } + this.doTestWrite(builder.toString()); + } +} diff --git a/src/test/java/com/alibaba/json/bvt/serializer/SerializeWriterTest_BrowserSecure.java b/src/test/java/com/alibaba/json/bvt/serializer/SerializeWriterTest_BrowserSecure.java new file mode 100644 index 000000000..fb56d297a --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/serializer/SerializeWriterTest_BrowserSecure.java @@ -0,0 +1,87 @@ +package com.alibaba.json.bvt.serializer; + +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; + +import junit.framework.TestCase; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; + +public class SerializeWriterTest_BrowserSecure extends TestCase { + + public void test_0() throws Exception { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 1024; ++i) { + buf.append('a'); + } + buf.append("中国"); + buf.append("\0"); + JSON.toJSONString(buf.toString(), SerializerFeature.BrowserSecure); + } + + public void test_1() throws Exception { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 1024; ++i) { + buf.append('a'); + } + buf.append("中国"); + buf.append("\0"); + + StringWriter out = new StringWriter(); + JSON.writeJSONStringTo(buf.toString(), out, SerializerFeature.BrowserSecure); + } + + public void test_zh() throws Exception { + Assert.assertEquals("\"\\u4E2D\\u56FD\"", JSON.toJSONString("中国", SerializerFeature.BrowserSecure)); + } + + public void test_all() throws Exception { + String value = ".,_~!@<>'\"\\/hello world 0123;汉字;\u2028\u2028\r\n