Skip to content

Instantly share code, notes, and snippets.

@devinrsmith
Last active February 2, 2017 23:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devinrsmith/51f10cfdebc7c7b803aa6df4f13b097c to your computer and use it in GitHub Desktop.
Save devinrsmith/51f10cfdebc7c7b803aa6df4f13b097c to your computer and use it in GitHub Desktop.
Unicode testing w/ jackson
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* Created by dsmith on 2/2/17.
*/
public class UnicodeTest {
// http://www.fileformat.info/info/unicode/char/1f5ef/index.htm
private static final String GOOD_UNICODE = "\uD83D\uDDEF";
// http://www.fileformat.info/info/unicode/char/d83d/index.htm
private static final String BAD_UNICODE = "\uD83D";
private static final String GOOD_JSON = "{\"value\":\"\\uD83D\\uDDEF\"}";
private static final String BAD_JSON = "{\"value\":\"\\uD83D\"}";
private static final StringHolder GOOD_HOLDER = new StringHolder(GOOD_UNICODE);
private static final StringHolder BAD_HOLDER = new StringHolder(BAD_UNICODE);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Test
public void utf8() throws Exception {
isValid(GOOD_UNICODE, StandardCharsets.UTF_8);
isInvalid(BAD_UNICODE, StandardCharsets.UTF_8);
}
@Test
public void utf16() throws Exception {
isValid(GOOD_UNICODE, StandardCharsets.UTF_16);
isInvalid(BAD_UNICODE, StandardCharsets.UTF_16);
}
@Test
public void jacksonFromObject() throws Exception {
isJsonEncodable(StringHolder.class, GOOD_HOLDER);
isNotJsonEncodable(StringHolder.class, BAD_HOLDER);
}
@Test
public void jacksonFromString() throws Exception {
isJsonDecodable(StringHolder.class, GOOD_JSON, GOOD_HOLDER);
isNotJsonDecodable(StringHolder.class, BAD_JSON);
}
private static class StringHolder {
@JsonProperty
private String value;
public StringHolder(String value) {
this.value = value;
}
StringHolder() {
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StringHolder that = (StringHolder) o;
return value.equals(that.value);
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public String toString() {
return "StringHolder{" +
"value='" + value + '\'' +
'}';
}
}
private static <T> void isJsonDecodable(Class<T> clazz, String input, T expected) {
try {
assertThat(OBJECT_MAPPER.readValue(input, clazz)).isEqualTo(expected);
} catch (IOException e) {
fail("Didn't expect failure", e);
}
}
private static <T> void isNotJsonDecodable(Class<T> clazz, String input) {
final T decoded;
try {
decoded = OBJECT_MAPPER.readValue(input, clazz);
} catch (IOException e) {
// this is what we want
return;
}
fail(String.format("Expected to not be decode, input=%s, obj=%s", input, decoded));
}
private static <T> void isJsonEncodable(Class<T> clazz, T input) {
final String output;
try {
output = OBJECT_MAPPER.writeValueAsString(input);
} catch (JsonProcessingException e) {
fail("Didn't expect failure", e);
return;
}
final T decoded;
try {
decoded = OBJECT_MAPPER.readValue(output, clazz);
} catch (IOException e) {
fail("Didn't expect failure", e);
return;
}
assertThat(decoded).isEqualTo(input);
}
private static <T> void isNotJsonEncodable(Class<T> clazz, T input) {
try {
final String output = OBJECT_MAPPER.writeValueAsString(input);
// hmm, we got output we shouldn't have!
// lets read it and see what happens
final T decoded;
try {
decoded = OBJECT_MAPPER.readValue(output, clazz);
} catch (IOException e) {
// okay, so we could write it, but not read it
fail(String.format("Can write json, but not read it, output=%s", output), e);
return;
}
if (decoded.equals(input)) {
fail(String.format(
"Can write / read json, and values are equal, but expected invalid input, output=%s",
output));
} else {
fail(String.format(
"Can write / read json, and values are not equal, but expected invalid input, output=%s",
output));
}
} catch (JsonProcessingException e) {
// yay, we expected this
}
}
private void isValid(String s, Charset charset) {
final byte[] bytes = s.getBytes(charset);
final String decoded = new String(bytes, charset);
assertThat(decoded).isEqualTo(s);
assertThat(charset.newEncoder().canEncode(s)).isTrue();
}
private void isInvalid(String s, Charset charset) {
final byte[] bytes = s.getBytes(charset);
final String decoded = new String(bytes, charset);
assertThat(decoded).isNotEqualTo(s);
assertThat(charset.newEncoder().canEncode(s)).isFalse();
}
}
@devinrsmith
Copy link
Author

Using Jackson 2.8.6:

java.lang.AssertionError: Can write / read json, and values are equal, but expected invalid input, output={"value":"?"}

and

java.lang.AssertionError: Expected to not be decode, input={"value":"\uD83D"}, obj=StringHolder{value='?'}.

I believe the IDE window is printing the '?', but the in memory string is "\uD83D" in both cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment