Skip to content

Commit fd5ca3b

Browse files
committed
Add part of #136
1 parent 63fd0ab commit fd5ca3b

File tree

3 files changed

+83
-3
lines changed

3 files changed

+83
-3
lines changed

release-notes/VERSION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ JSON library.
1818

1919
#86: Allow inclusion of request body for JsonParseException
2020
(contributed by LokeshN)
21+
#136: Add `JsonpCharacterEscapes` for easier handling of potential problems
22+
with JSONP and rare but technically allowed \u2028 and \u2029 linefeed characters
2123
#117: Add `JsonParser.Feature.ALLOW_MISSING_VALUES` to support for missing values
2224
(contributed by LokeshN)
2325
#253: Add `JsonGenerator. writeEmbeddedObject()` to allow writes of opaque native types
@@ -35,7 +37,6 @@ JSON library.
3537
#285: Add `JsonParser.getText(Writer)`
3638
(contributed by LokesN)
3739
#290: Add `JsonGenerator.canWriteFormattedNumbers()` for introspection
38-
3940
- Add `JsonParser.currentToken()` and `JsonParser.currentTokenId()` as replacements
4041
for `getCurrentToken()` and `getCurrentTokenId()`, respectively. Existing methods
4142
will likely be deprecated in 2.9.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.fasterxml.jackson.core;
2+
3+
import com.fasterxml.jackson.core.io.CharacterEscapes;
4+
import com.fasterxml.jackson.core.io.SerializedString;
5+
6+
/**
7+
* Convenience {@link CharacterEscapes} implementation that escapes
8+
* Unicode characters `0x2028` and `0x2029` (in addition to characters
9+
* escaped otherwise), which are apparently considered linefeeds as
10+
* per newer Javascript specifications, and consequently problematic
11+
* when using JSONP (see https://en.wikipedia.org/wiki/JSONP).
12+
*
13+
* @since 2.8
14+
*/
15+
public class JsonpCharacterEscapes extends CharacterEscapes
16+
{
17+
private static final long serialVersionUID = 1L;
18+
19+
private static final int[] asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
20+
private static final SerializedString escapeFor2028 = new SerializedString("\\u2028");
21+
private static final SerializedString escapeFor2029 = new SerializedString("\\u2029");
22+
23+
private static final JsonpCharacterEscapes sInstance = new JsonpCharacterEscapes();
24+
25+
public static JsonpCharacterEscapes instance() {
26+
return sInstance;
27+
}
28+
29+
@Override
30+
public SerializableString getEscapeSequence(int ch)
31+
{
32+
switch (ch) {
33+
case 0x2028:
34+
return escapeFor2028;
35+
case 0x2029:
36+
return escapeFor2029;
37+
default:
38+
return null;
39+
}
40+
}
41+
42+
@Override
43+
public int[] getEscapeCodesForAscii() {
44+
return asciiEscapes;
45+
}
46+
}

src/test/java/com/fasterxml/jackson/core/json/TestCustomEscaping.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public void testAboveAsciiEscapeWithUTF8Stream() throws Exception
7878
}
7979

8080
// // // Tests for [JACKSON-106]
81-
81+
8282
public void testEscapeCustomWithReader() throws Exception
8383
{
8484
_testEscapeCustom(false); // reader
@@ -88,7 +88,40 @@ public void testEscapeCustomWithUTF8Stream() throws Exception
8888
{
8989
_testEscapeCustom(true); // stream (utf-8)
9090
}
91-
91+
92+
public void testJsonpEscapes() throws Exception {
93+
_testJsonpEscapes(false);
94+
_testJsonpEscapes(true);
95+
}
96+
97+
@SuppressWarnings("resource")
98+
private void _testJsonpEscapes(boolean useStream) throws Exception
99+
{
100+
JsonFactory f = new JsonFactory();
101+
f.setCharacterEscapes(JsonpCharacterEscapes.instance());
102+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
103+
JsonGenerator g;
104+
105+
// First: output normally; should not add escaping
106+
if (useStream) {
107+
g = f.createGenerator(bytes, JsonEncoding.UTF8);
108+
} else {
109+
g = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
110+
}
111+
final String VALUE_TEMPLATE = "String with JS 'linefeeds': %s and %s...";
112+
final String INPUT_VALUE = String.format(VALUE_TEMPLATE, "\u2028", "\u2029");
113+
114+
g.writeStartArray();
115+
g.writeString(INPUT_VALUE);
116+
g.writeEndArray();
117+
g.close();
118+
119+
String json = bytes.toString("UTF-8");
120+
assertEquals(String.format("[%s]",
121+
quote(String.format(VALUE_TEMPLATE, "\\u2028", "\\u2029"))),
122+
json);
123+
}
124+
92125
/*
93126
/********************************************************
94127
/* Secondary test methods

0 commit comments

Comments
 (0)