diff --git a/gson/src/main/java/com/google/gson/Escaper.java b/gson/src/main/java/com/google/gson/Escaper.java index 34b0c2cb..24636534 100644 --- a/gson/src/main/java/com/google/gson/Escaper.java +++ b/gson/src/main/java/com/google/gson/Escaper.java @@ -54,19 +54,19 @@ final class Escaper { htmlEscapeSet.add('>'); htmlEscapeSet.add('&'); htmlEscapeSet.add('='); - htmlEscapeSet.add('\''); + htmlEscapeSet.add('\''); // htmlEscapeSet.add('/'); -- Removing slash for now since it causes some incompatibilities HTML_ESCAPE_CHARS = Collections.unmodifiableSet(htmlEscapeSet); } private final boolean escapeHtmlCharacters; - + Escaper(boolean escapeHtmlCharacters) { this.escapeHtmlCharacters = escapeHtmlCharacters; } - + public String escapeJsonString(CharSequence plainText) { - StringBuffer escapedString = new StringBuffer(plainText.length() + 20); + StringBuilder escapedString = new StringBuilder(plainText.length() + 20); try { escapeJsonString(plainText, escapedString); } catch (IOException e) { @@ -75,14 +75,14 @@ final class Escaper { return escapedString.toString(); } - private void escapeJsonString(CharSequence plainText, StringBuffer out) throws IOException { + private void escapeJsonString(CharSequence plainText, StringBuilder out) throws IOException { int pos = 0; // Index just past the last char in plainText written to out. int len = plainText.length(); - + for (int charCount, i = 0; i < len; i += charCount) { int codePoint = Character.codePointAt(plainText, i); charCount = Character.charCount(codePoint); - + if (!isControlCharacter(codePoint) && !mustEscapeCharInJsString(codePoint)) { continue; } @@ -121,7 +121,7 @@ final class Escaper { } out.append(plainText, pos, len); } - + private boolean mustEscapeCharInJsString(int codepoint) { if (!Character.isSupplementaryCodePoint(codepoint)) { char c = (char) codepoint; diff --git a/gson/src/main/java/com/google/gson/JsonFieldNameValidator.java b/gson/src/main/java/com/google/gson/JsonFieldNameValidator.java deleted file mode 100644 index 07027b92..00000000 --- a/gson/src/main/java/com/google/gson/JsonFieldNameValidator.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * 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 "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import com.google.gson.internal.$Gson$Preconditions; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * This class can be used to check the validity of a JSON field name. - * - *

The primary use of this object is to ensure that any Java fields that use the - * {@link com.google.gson.annotations.SerializedName} annotation is providing valid JSON - * field names. This will make the code fail-fast rather than letting the invalid - * field name propagate to the client and it fails to parse.

- * - * @author Joel Leitch - */ -class JsonFieldNameValidator { - private static final String COMMON_PATTERN = "[a-zA-Z][a-zA-Z0-9\\ \\$_\\-]*$"; - - private static final Pattern JSON_FIELD_NAME_PATTERN = - Pattern.compile("(^" + COMMON_PATTERN + ")|(^[\\$_]" + COMMON_PATTERN + ")"); - - - /** - * Performs validation on the JSON field name to ensure it is a valid field name. - * - * @param fieldName the name of the field to validate - * @return {@code fieldName} if it is a valid JSON field name - * @throws IllegalArgumentException if the field name is an invalid JSON field name - */ - public String validate(String fieldName) { - $Gson$Preconditions.checkNotNull(fieldName); - $Gson$Preconditions.checkArgument(!"".equals(fieldName.trim())); - - Matcher matcher = JSON_FIELD_NAME_PATTERN.matcher(fieldName); - if (!matcher.matches()) { - throw new IllegalArgumentException(fieldName + " is not a valid JSON field name."); - } - return fieldName; - } -} diff --git a/gson/src/main/java/com/google/gson/SerializedNameAnnotationInterceptingNamingPolicy.java b/gson/src/main/java/com/google/gson/SerializedNameAnnotationInterceptingNamingPolicy.java index b8a500ca..8143a6f7 100644 --- a/gson/src/main/java/com/google/gson/SerializedNameAnnotationInterceptingNamingPolicy.java +++ b/gson/src/main/java/com/google/gson/SerializedNameAnnotationInterceptingNamingPolicy.java @@ -19,20 +19,22 @@ package com.google.gson; import com.google.gson.annotations.SerializedName; /** - * A {@link FieldNamingStrategy2} that acts as a chain of responsibility. If the - * {@link com.google.gson.annotations.SerializedName} annotation is applied to a field then this - * strategy will translate the name to the {@code serializedName.value()}; otherwise it delegates - * to the wrapped {@link FieldNamingStrategy2}. + * A {@link FieldNamingStrategy2} that acts as a chain of responsibility. If the + * {@link com.google.gson.annotations.SerializedName} annotation is applied to a + * field then this strategy will translate the name to the {@code + * serializedName.value()}; otherwise it delegates to the wrapped + * {@link FieldNamingStrategy2}. * - *

NOTE: this class performs JSON field name validation for any of the fields marked with - * an {@code @SerializedName} annotation.

+ *

+ * NOTE: this class performs JSON field name validation for any of the fields + * marked with an {@code @SerializedName} annotation. + *

* * @see SerializedName * * @author Joel Leitch */ final class SerializedNameAnnotationInterceptingNamingPolicy implements FieldNamingStrategy2 { - private static final JsonFieldNameValidator fieldNameValidator = new JsonFieldNameValidator(); private final FieldNamingStrategy2 delegate; SerializedNameAnnotationInterceptingNamingPolicy(FieldNamingStrategy2 delegate) { @@ -41,7 +43,6 @@ final class SerializedNameAnnotationInterceptingNamingPolicy implements FieldNam public String translateName(FieldAttributes f) { SerializedName serializedName = f.getAnnotation(SerializedName.class); - return serializedName == null ? delegate.translateName(f) - : fieldNameValidator.validate(serializedName.value()); + return serializedName == null ? delegate.translateName(f) : serializedName.value(); } } diff --git a/gson/src/test/java/com/google/gson/JsonFieldNameValidatorTest.java b/gson/src/test/java/com/google/gson/JsonFieldNameValidatorTest.java deleted file mode 100644 index 91054d9f..00000000 --- a/gson/src/test/java/com/google/gson/JsonFieldNameValidatorTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * 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 "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import junit.framework.TestCase; - -/** - * Unit tests for the {@link JsonFieldNameValidator} class. - * - * @author Joel Leitch - */ -public class JsonFieldNameValidatorTest extends TestCase { - - private JsonFieldNameValidator validator; - - @Override - protected void setUp() throws Exception { - super.setUp(); - validator = new JsonFieldNameValidator(); - } - - public void testValidFieldBeginsWithDollarSign() throws Exception { - String fieldName = "$abc"; - assertEquals(fieldName, validator.validate(fieldName)); - } - - public void testValidFieldBeginsWithUnderscore() throws Exception { - String fieldName = "_abc"; - assertEquals(fieldName, validator.validate(fieldName)); - } - - public void testValidFieldBeginsWithLetter() throws Exception { - String fieldName = "abc"; - assertEquals(fieldName, validator.validate(fieldName)); - } - - public void testValidFieldMixingLetter() throws Exception { - String fieldName = "$abc_12v$34"; - assertEquals(fieldName, validator.validate(fieldName)); - } - - public void testInvalidFieldStartingWithNumbers() throws Exception { - try { - validator.validate("1abc"); - fail("Json field name can not start with a number"); - } catch (IllegalArgumentException expected) { } - } - - public void testInvalidFieldStartingTwoDollarSigns() throws Exception { - try { - validator.validate("$$abc"); - fail("Json field name can not start with a double dollar sign"); - } catch (IllegalArgumentException expected) { } - } - - public void testInvalidFieldStartingTwoUnderscores() throws Exception { - try { - validator.validate("__abc"); - fail("Json field name can not start with a double underscore"); - } catch (IllegalArgumentException expected) { } - } - - public void testInvalidFieldStartingDollarUnderscore() throws Exception { - try { - validator.validate("$_abc"); - fail("Json field name can not start with two non-alphabet characters"); - } catch (IllegalArgumentException expected) { } - } - - public void testJavaAndJsKeywordAsFieldName() throws Exception { - String fieldName = "break"; - assertEquals(fieldName, validator.validate(fieldName)); - } - - public void testInvalidCharacters() throws Exception { - try { - validator.validate("abc.123"); - fail("Json field name can not contain a period character"); - } catch (IllegalArgumentException expected) { } - } - - public void testDashesInFieldName() throws Exception { - String fieldName = "test-field-name"; - assertEquals(fieldName, validator.validate(fieldName)); - } - - public void testSpacesInFieldName() throws Exception { - String fieldName = "test field name"; - assertEquals(fieldName, validator.validate(fieldName)); - } - - public void testSpacesInBeginningOfName() throws Exception { - try { - validator.validate(" testFieldName"); - fail("Json field name can not contain a period character"); - } catch (IllegalArgumentException expected) { } - } -} diff --git a/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java b/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java index 07f3c231..a7525fe2 100644 --- a/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java +++ b/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java @@ -15,8 +15,6 @@ */ package com.google.gson.functional; -import java.lang.reflect.Field; - import com.google.gson.FieldNamingPolicy; import com.google.gson.FieldNamingStrategy; import com.google.gson.Gson; @@ -27,6 +25,8 @@ import com.google.gson.common.TestTypes.StringWrapper; import junit.framework.TestCase; +import java.lang.reflect.Field; + /** * Functional tests for naming policies. * @@ -34,7 +34,6 @@ import junit.framework.TestCase; * @author Joel Leitch */ public class NamingPolicyTest extends TestCase { - private GsonBuilder builder; @Override @@ -56,7 +55,7 @@ public class NamingPolicyTest extends TestCase { StringWrapper deserializedObject = gson.fromJson(target, StringWrapper.class); assertEquals("someValue", deserializedObject.someConstantStringInstanceField); } - + public void testGsonWithLowerCaseDashPolicySerialization() { Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create(); StringWrapper target = new StringWrapper("blah"); @@ -70,7 +69,7 @@ public class NamingPolicyTest extends TestCase { StringWrapper deserializedObject = gson.fromJson(target, StringWrapper.class); assertEquals("someValue", deserializedObject.someConstantStringInstanceField); } - + public void testGsonWithLowerCaseUnderscorePolicySerialization() { Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .create(); @@ -101,18 +100,18 @@ public class NamingPolicyTest extends TestCase { gson.fromJson(expected.getExpectedJson(), ClassWithSerializedNameFields.class); assertEquals(expected.f, actual.f); } - + public void testGsonDuplicateNameUsingSerializedNameFieldNamingPolicySerialization() { Gson gson = builder.create(); ClassWithDuplicateFields target = new ClassWithDuplicateFields(10); String actual = gson.toJson(target); assertEquals("{\"a\":10}", actual); - + target = new ClassWithDuplicateFields(3.0D); actual = gson.toJson(target); assertEquals("{\"a\":3.0}", actual); } - + public void testGsonWithUpperCamelCaseSpacesPolicySerialiation() { Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES) .create(); @@ -120,7 +119,7 @@ public class NamingPolicyTest extends TestCase { assertEquals("{\"Some Constant String Instance Field\":\"" + target.someConstantStringInstanceField + "\"}", gson.toJson(target)); } - + public void testGsonWithUpperCamelCaseSpacesPolicyDeserialiation() { Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES) .create(); @@ -136,6 +135,16 @@ public class NamingPolicyTest extends TestCase { assertEquals("{\"A\":10}", actual); } + public void testComplexFieldNameStrategy() throws Exception { + Gson gson = new Gson(); + String json = gson.toJson(new ClassWithComplexFieldName(10)); + String escapedFieldName = "@value\\\"_s$\\\\"; + assertEquals("{\"" + escapedFieldName + "\":10}", json); + + ClassWithComplexFieldName obj = gson.fromJson(json, ClassWithComplexFieldName.class); + assertEquals(10, obj.value); + } + private static class UpperCaseNamingStrategy implements FieldNamingStrategy { public String translateName(Field f) { return f.getName().toUpperCase(); @@ -146,18 +155,26 @@ public class NamingPolicyTest extends TestCase { private static class ClassWithDuplicateFields { public Integer a; @SerializedName("a") public Double b; - + public ClassWithDuplicateFields(Integer a) { this(a, null); } - + public ClassWithDuplicateFields(Double b) { this(null, b); } - + public ClassWithDuplicateFields(Integer a, Double b) { this.a = a; this.b = b; } } + + private static class ClassWithComplexFieldName { + @SerializedName("@value\"_s$\\") public final long value; + + ClassWithComplexFieldName(long value) { + this.value = value; + } + } }