diff --git a/src/main/java/org/hisp/dhis/jsontree/JsonAppender.java b/src/main/java/org/hisp/dhis/jsontree/JsonAppender.java index ffbd312..c898bdc 100644 --- a/src/main/java/org/hisp/dhis/jsontree/JsonAppender.java +++ b/src/main/java/org/hisp/dhis/jsontree/JsonAppender.java @@ -104,15 +104,26 @@ void appendEscaped( CharSequence str ) { return; } append( '"' ); - str.chars().forEachOrdered( c -> { - if ( c == '"' || c == '\\' || c < ' ' ) { - appendChar.accept( '\\' ); - } - appendChar.accept( (char) c ); - } ); + str.chars().forEachOrdered( this::appendEscaped ); append( '"' ); } + private void appendEscaped( int c ) { + switch ( c ) { + case '\b' -> append( "\\b" ); + case '\f' -> append( "\\f" ); + case '\n' -> append( "\\n" ); + case '\r' -> append( "\\r" ); + case '\t' -> append( "\\t" ); + case '"' -> append( "\\\"" ); + case '\\' -> append( "\\\\" ); + case -31 -> append( "\\u%04X".formatted( c ) ); + case 0x2028 -> append( "\\u2028" ); + case 0x2029 -> append( "\\u2029" ); + default -> appendChar.accept( (char) c ); + } + } + private void beginLevel( char c ) { append( c ); hasChildrenAtLevel[++level] = false; diff --git a/src/main/java/org/hisp/dhis/jsontree/JuonAppender.java b/src/main/java/org/hisp/dhis/jsontree/JuonAppender.java index 767d51d..370cd49 100644 --- a/src/main/java/org/hisp/dhis/jsontree/JuonAppender.java +++ b/src/main/java/org/hisp/dhis/jsontree/JuonAppender.java @@ -213,12 +213,23 @@ private void append(char c) { void appendEscaped(@Surly CharSequence str ) { append( '\'' ); - str.chars().forEachOrdered( c -> { - if ( c == '\'' || c == '\\' || c < ' ' ) { - append( '\\' ); - } - append( (char) c ); - } ); + str.chars().forEachOrdered( this::appendEscaped ); append( '\'' ); } + + private void appendEscaped( int c ) { + switch ( c ) { + case '\b' -> append( "\\b" ); + case '\f' -> append( "\\f" ); + case '\n' -> append( "\\n" ); + case '\r' -> append( "\\r" ); + case '\t' -> append( "\\t" ); + case '\'' -> append( "\\'" ); + case '\\' -> append( "\\\\" ); + case -31 -> append( "\\u%04X".formatted( c ) ); + case 0x2028 -> append( "\\u2028" ); + case 0x2029 -> append( "\\u2029" ); + default -> append( (char) c ); + } + } } diff --git a/src/test/java/org/hisp/dhis/jsontree/JsonAppenderTest.java b/src/test/java/org/hisp/dhis/jsontree/JsonAppenderTest.java index 3f33253..32d9c4b 100644 --- a/src/test/java/org/hisp/dhis/jsontree/JsonAppenderTest.java +++ b/src/test/java/org/hisp/dhis/jsontree/JsonAppenderTest.java @@ -269,6 +269,25 @@ void testArray_JsonNode() { .addElement( JsonNode.of( "[\"a\",\"b\"]" ) ) ) ); } + @Test + void testNewlines() { + //language=JSON + String json = """ + {"id":"woOg1dUFoX0","time":1738685143915,"message":"com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\\n at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]","level":"ERROR"}"""; + JsonObject msg = JsonMixed.of( json ); + String message = msg.getString( "message" ).string(); + assertEquals( """ + com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false') + at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]""", + message ); + JsonObject actual = Json.object( obj -> + obj.addString( "id", "woOg1dUFoX0" ) + .addNumber( "time", 1738685143915L ) + .addString( "message", message ) + .addString( "level", "ERROR" )); + assertEquals( json, actual.toJson() ); + } + private static void assertJson( String expected, JsonNode actual ) { assertEquals( expected.replace( '\'', '"' ), actual.getDeclaration() ); } diff --git a/src/test/java/org/hisp/dhis/jsontree/JsonTest.java b/src/test/java/org/hisp/dhis/jsontree/JsonTest.java index 22a95c9..f6f2553 100644 --- a/src/test/java/org/hisp/dhis/jsontree/JsonTest.java +++ b/src/test/java/org/hisp/dhis/jsontree/JsonTest.java @@ -35,8 +35,9 @@ void testOf_Boolean() { void testOf_String() { assertJson( "null", Json.of( (String) null ) ); assertJson( "\"hello\"", Json.of( "hello" ) ); - assertJson( "\"hello\\\n" - + "world\"", Json.of( "hello\nworld" ) ); + JsonString hello = Json.of( "hello\nworld" ); + assertJson( "\"hello\\nworld\"", hello ); + assertEquals( "hello\nworld", hello.string() ); } @Test