Skip to content

Commit

Permalink
add cfg for hex values if we want upper or lowercase letters (#819)
Browse files Browse the repository at this point in the history
  • Loading branch information
Richie94 authored Oct 4, 2022
1 parent 19f1cfc commit 4c2f048
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 10 deletions.
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() {
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
*/
@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);
}

}

0 comments on commit 4c2f048

Please sign in to comment.