Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cfg for hex values if we want upper or lowercase letters #819

Merged
17 changes: 15 additions & 2 deletions src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,21 @@ public enum Feature {
* @deprecated Use {@link com.fasterxml.jackson.core.StreamWriteFeature#USE_FAST_DOUBLE_WRITER} instead
*/
@Deprecated
USE_FAST_DOUBLE_WRITER(false)
;
USE_FAST_DOUBLE_WRITER(false),

/**
* Feature that specifies that hex values are encoded with capital letters.
*<p>
* Can be disabled to have a better possibility to compare between other Json
* writer libraries, such as JSON.stringify from Javascript.
*<p>
* Feature is enabled by default.
*
* @since 2.14
* @deprecated Use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_HEX_UPPER_CASE} instead
*/
@Deprecated
WRITE_HEX_UPPER_CASE(true);

private final boolean _defaultState;
private final int _mask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public JsonLocation startLocation(ContentReference srcRef) {
public JsonLocation getStartLocation(Object srcRef) {
return JsonLocation.NA;
}

/**
* Overridden to provide developer readable "JsonPath" representation
* of the context.
Expand Down
20 changes: 16 additions & 4 deletions src/main/java/com/fasterxml/jackson/core/io/CharTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
public final class CharTypes
{
protected final static char[] HC = "0123456789ABCDEF".toCharArray();
protected final static char[] HClower = "0123456789abcdef".toCharArray();
protected final static byte[] HB;
protected final static byte[] HBlower;
static {
int len = HC.length;
HB = new byte[len];
HBlower = new byte[len];
for (int i = 0; i < len; ++i) {
HB[i] = (byte) HC[i];
HBlower[i] = (byte) HClower[i];
}
}

Expand Down Expand Up @@ -242,6 +246,7 @@ public static char hexToChar(int ch)
return HC[ch];
}


/**
* Helper method for appending JSON-escaped version of contents
* into specific {@link StringBuilder}, using default JSON specification
Expand All @@ -251,8 +256,7 @@ public static char hexToChar(int ch)
*
* @param content Unescaped String value to append with escaping applied
*/
public static void appendQuoted(StringBuilder sb, String content)
{
public static void appendQuoted(StringBuilder sb, String content) {
final int[] escCodes = sOutputEscapes128;
int escLen = escCodes.length;
for (int i = 0, len = content.length(); i < len; ++i) {
Expand Down Expand Up @@ -285,11 +289,19 @@ public static void appendQuoted(StringBuilder sb, String content)
}

public static char[] copyHexChars() {
Richie94 marked this conversation as resolved.
Show resolved Hide resolved
return (char[]) HC.clone();
return copyHexChars(true);
}

public static char[] copyHexChars(boolean uppercase) {
return (uppercase) ? HC.clone() : HClower.clone();
}

public static byte[] copyHexBytes() {
return (byte[]) HB.clone();
return copyHexBytes(true);
}

public static byte[] copyHexBytes(boolean uppercase) {
return (uppercase) ? HB.clone() : HBlower.clone();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ public abstract class JsonGeneratorImpl extends GeneratorBase
*/
protected boolean _cfgUnqNames;


/**
* Write Hex values with uppercase letters
*
* @since 2.14
*/
protected boolean _cfgWriteHexUppercase;

/*
/**********************************************************
/* Life-cycle
Expand All @@ -117,6 +125,7 @@ public JsonGeneratorImpl(IOContext ctxt, int features, ObjectCodec codec)
// inlined `setHighestNonEscapedChar()`
_maximumNonEscapedChar = 127;
}
_cfgWriteHexUppercase = Feature.WRITE_HEX_UPPER_CASE.enabledIn(features);
_cfgUnqNames = !Feature.QUOTE_FIELD_NAMES.enabledIn(features);
}

Expand All @@ -143,6 +152,8 @@ public JsonGenerator enable(Feature f) {
super.enable(f);
if (f == Feature.QUOTE_FIELD_NAMES) {
_cfgUnqNames = false;
} else if ( f == Feature.WRITE_HEX_UPPER_CASE) {
_cfgWriteHexUppercase = true;
}
return this;
}
Expand All @@ -153,6 +164,8 @@ public JsonGenerator disable(Feature f) {
super.disable(f);
if (f == Feature.QUOTE_FIELD_NAMES) {
_cfgUnqNames = true;
} else if ( f == Feature.WRITE_HEX_UPPER_CASE) {
_cfgWriteHexUppercase = false;
}
return this;
}
Expand All @@ -162,6 +175,7 @@ public JsonGenerator disable(Feature f) {
protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures) {
super._checkStdFeatureChanges(newFeatureFlags, changedFeatures);
_cfgUnqNames = !Feature.QUOTE_FIELD_NAMES.enabledIn(newFeatureFlags);
_cfgWriteHexUppercase = Feature.WRITE_HEX_UPPER_CASE.enabledIn(newFeatureFlags);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public Object getCurrentValue() {
public void setCurrentValue(Object v) {
_currentValue = v;
}

/*
/**********************************************************
/* Factory methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ public enum JsonWriteFeature
@SuppressWarnings("deprecation")
ESCAPE_NON_ASCII(false, JsonGenerator.Feature.ESCAPE_NON_ASCII),

/**
* Feature that specifies that hex values are encoded with capital letters.
*<p>
* Can be disabled to have a better possibility to compare between other Json
* writer libraries, such as JSON.stringify from Javascript.
*<p>
* Feature is enabled by default.
*
* @since 2.14
*/
Richie94 marked this conversation as resolved.
Show resolved Hide resolved
@SuppressWarnings("deprecation")
WRITE_HEX_UPPER_CASE(true, JsonGenerator.Feature.WRITE_HEX_UPPER_CASE),

//23-Nov-2015, tatu: for [core#223], if and when it gets implemented
/*
* Feature that specifies handling of UTF-8 content that contains
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,17 @@ public class UTF8JsonGenerator
// intermediate copies only made up to certain length...
private final static int MAX_BYTES_TO_BUFFER = 512;

private final static byte[] HEX_CHARS = CharTypes.copyHexBytes();
private final static byte[] HEX_CHARS_UPPER = CharTypes.copyHexBytes(true);
private final static byte[] HEX_CHARS_LOWER = CharTypes.copyHexBytes(false);

private final static byte[] NULL_BYTES = { 'n', 'u', 'l', 'l' };
private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' };
private final static byte[] FALSE_BYTES = { 'f', 'a', 'l', 's', 'e' };

private byte[] getHexChars() {
return _cfgWriteHexUppercase ? HEX_CHARS_UPPER : HEX_CHARS_LOWER;
}

/*
/**********************************************************
/* Configuration
Expand Down Expand Up @@ -2136,6 +2141,7 @@ protected final void _outputSurrogates(int surr1, int surr2) throws IOException
*/
private final int _outputMultiByteChar(int ch, int outputPtr) throws IOException
{
byte[] HEX_CHARS = getHexChars();
byte[] bbuf = _outputBuffer;
if (ch >= SURR1_FIRST && ch <= SURR2_LAST) { // yes, outside of BMP; add an escape
// 23-Nov-2015, tatu: As per [core#223], may or may not want escapes;
Expand Down Expand Up @@ -2175,6 +2181,7 @@ private final void _writeNull() throws IOException
private int _writeGenericEscape(int charToEscape, int outputPtr) throws IOException
{
final byte[] bbuf = _outputBuffer;
byte[] HEX_CHARS = getHexChars();
bbuf[outputPtr++] = BYTE_BACKSLASH;
bbuf[outputPtr++] = BYTE_u;
if (charToEscape > 0xFF) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ public class WriterBasedJsonGenerator
{
protected final static int SHORT_WRITE = 32;

protected final static char[] HEX_CHARS = CharTypes.copyHexChars();
protected final static char[] HEX_CHARS_UPPER = CharTypes.copyHexChars(true);
protected final static char[] HEX_CHARS_LOWER = CharTypes.copyHexChars(false);

private char[] getHexChars() {
return _cfgWriteHexUppercase ? HEX_CHARS_UPPER : HEX_CHARS_LOWER;
}

/*
/**********************************************************
Expand Down Expand Up @@ -1814,6 +1819,7 @@ private void _prependOrWriteCharacterEscape(char ch, int escCode)
return;
}
if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
char[] HEX_CHARS = getHexChars();
if (_outputTail >= 6) { // fits, prepend to buffer
char[] buf = _outputBuffer;
int ptr = _outputTail - 6;
Expand Down Expand Up @@ -1902,6 +1908,7 @@ private int _prependOrWriteCharacterEscape(char[] buffer, int ptr, int end,
return ptr;
}
if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
char[] HEX_CHARS = getHexChars();
if (ptr > 5 && ptr < end) { // fits, prepend to buffer
ptr -= 6;
buffer[ptr++] = '\\';
Expand Down Expand Up @@ -1980,6 +1987,7 @@ private void _appendCharacterEscape(char ch, int escCode)
}
int ptr = _outputTail;
char[] buf = _outputBuffer;
char[] HEX_CHARS = getHexChars();
buf[ptr++] = '\\';
buf[ptr++] = 'u';
// We know it's a control char, so only the last 2 chars are non-0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import java.io.ByteArrayOutputStream;

import static com.fasterxml.jackson.core.json.JsonWriteFeature.WRITE_HEX_UPPER_CASE;

public class UTF8GeneratorTest extends BaseTest
{
private final JsonFactory JSON_F = new JsonFactory();
Expand Down Expand Up @@ -114,4 +116,33 @@ public void testFilteringWithEscapedChars() throws Exception
assertNull(p.nextToken());
p.close();
}

public void testHexLowercase() throws Exception
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
JsonFactory factory = JsonFactory.builder().disable(WRITE_HEX_UPPER_CASE).build();
JsonGenerator gen = factory.createGenerator(bytes);
String str = "\u001b";
gen.writeString(str);
gen.flush();
gen.close();

String result = bytes.toString();
assertEquals("\"\\u001b\"", result);
}

public void testHexUppercase() throws Exception
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
JsonFactory factory = JsonFactory.builder().enable(WRITE_HEX_UPPER_CASE).build();
JsonGenerator gen = factory.createGenerator(bytes);
String str = "\u001b";
gen.writeString(str);
gen.flush();
gen.close();

String result = bytes.toString();
assertEquals("\"\\u001B\"", result);
}

}