Last active
February 2, 2017 23:13
-
-
Save devinrsmith/51f10cfdebc7c7b803aa6df4f13b097c to your computer and use it in GitHub Desktop.
Unicode testing w/ jackson
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.