Skip to content
This repository has been archived by the owner on Nov 5, 2019. It is now read-only.

Cannot read some "pretty" documents #60

Closed
lehcim opened this issue Sep 29, 2015 · 6 comments
Closed

Cannot read some "pretty" documents #60

lehcim opened this issue Sep 29, 2015 · 6 comments
Milestone

Comments

@lehcim
Copy link

lehcim commented Sep 29, 2015

I'm facing an issue reading a pretty-printed document from a file with afterburner 2.6.2.
The input is a valid JSON document, but deserialization fails with a message saying that a colon is not found:

com.fasterxml.jackson.core.JsonParseException: Unexpected character ('[' (code 91)): was expecting a colon to separate field name and value
 at [Source: foo.json; line: 3, column: 6]

The document is as follow (please note the space before the colon):

{
    "foos" :
    [
    ]
}

and the java code looks like this :

class FooList {
    public List<Object> foos ;
}

@Test
public void testBurnerFile() throws IOException {
    ObjectMapper burner = new ObjectMapper().registerModule(new AfterburnerModule()) ;
    burner.readValue(filename, FooList.class) ;
}

Using a raw ObjectMapper, there is no error reading a value from any source (String, File, Reader, Stream). However, after registering the AfterburnerModule, the deserialization fails reading from a File or a Stream (but other sources are ok...)

If you edit the document and remove the space before the colon, then everything is fine with Afterburner. Of course, this is only feasible for testing purpose, in real life I can't change the formatting on the server producing the JSON document...

Here is the full stack trace:

com.fasterxml.jackson.core.JsonParseException: Unexpected character ('[' (code 91)): was expecting a colon to separate field name and value
 at [Source: foo.json; line: 3, column: 6]
        at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1581)
        at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:533)
        at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:462)
        at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._skipColon2(UTF8StreamJsonParser.java:3006)
        at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._skipColonFast(UTF8StreamJsonParser.java:1104)
        at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextFieldName(UTF8StreamJsonParser.java:937)
        at com.fasterxml.jackson.module.afterburner.deser.SuperSonicBeanDeserializer.deserialize(SuperSonicBeanDeserializer.java:147)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3731)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2618)
        at afterburner.AfterburnerTest.testBurnerFile(AfterburnerTest.java:70)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
        at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
@lehcim
Copy link
Author

lehcim commented Sep 29, 2015

Here is the complete repro Junit test:

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;

class FooList {
    public List<Object> foos ;
}

public class AfterburnerIssue60Test {

        String content = 
        "{\n" +
        "    \"foos\" :\n" + 
        "    [\n" + 
        "    ]\n" +
        "}";
        File filename = new File("foo.json") ;

        @Before
        public void setup()  throws IOException {
            try (FileWriter writer = new FileWriter(filename)) {
                writer.write(content);
            }
        }

        @Test
        public void testMapperString() throws IOException {
            ObjectMapper mapper = new ObjectMapper() ;
            mapper.readValue(content, FooList.class) ;
        }

        @Test
        public void testMapperFile() throws IOException {
            ObjectMapper mapper = new ObjectMapper() ;
            mapper.readValue(filename, FooList.class) ;
        }

        @Test
        public void testMapperReader() throws IOException {
            ObjectMapper mapper = new ObjectMapper() ;
            mapper.readValue(new StringReader(content), FooList.class) ;
        }

        @Test
        public void testMapperStream() throws IOException {
            ObjectMapper mapper = new ObjectMapper() ;
            mapper.readValue(new ByteArrayInputStream(content.getBytes()), FooList.class) ;
        }

        @Test
        public void testBurnerString() throws IOException {
            ObjectMapper burner = new ObjectMapper().registerModule(new AfterburnerModule()) ;
            burner.readValue(content, FooList.class) ;
        }

        @Test
        public void testBurnerFile() throws IOException {
            ObjectMapper burner = new ObjectMapper().registerModule(new AfterburnerModule()) ;
            burner.readValue(filename, FooList.class) ;
        }

        @Test
        public void testBurnerReader() throws IOException {
            ObjectMapper burner = new ObjectMapper().registerModule(new AfterburnerModule()) ;          
            burner.readValue(new StringReader(content), FooList.class) ;
        }

        @Test
        public void testBurnerStream() throws IOException {
            ObjectMapper burner = new ObjectMapper().registerModule(new AfterburnerModule()) ;
            burner.readValue(new ByteArrayInputStream(content.getBytes()), FooList.class) ;
        }       
}

and the results:

Tests in error:
  testBurnerFile(afterburner.AfterburnerIssue60Test): Unexpected character ('[' (code 91)): was expecting a colon to separate field name and value
  testBurnerStream(afterburner.AfterburnerIssue60Test): Unexpected character ('[' (code 91)): was expecting a colon to separate field name and value

Tests run: 8, Failures: 0, Errors: 2, Skipped: 0

Hope this helps...

@cowtowncoder
Copy link
Member

Very interesting... thank you for reporting this, with a unit test.

I suspect this is due to Afterburner calling slightly different parsing methods, and the actual issue being within JSON parser (probably within nextFieldName(SerializableString) implementation).
I should be able to get this fixed for 2.6.3.

@lehcim
Copy link
Author

lehcim commented Sep 30, 2015

Yes, I tried to understand the problem using the debugger, but the solution was not obvious for me ...
Thank you for supporting this issue.

@cowtowncoder
Copy link
Member

I can reproduce the problem locally, and it does indeed only affect byte-backed parser, which explain why some other sources (like String) would not exhibit it. Codepaths are optimized quite differently.

@lehcim
Copy link
Author

lehcim commented Oct 1, 2015

Thank you very much !

@lehcim
Copy link
Author

lehcim commented Oct 14, 2015

Confirmed : 2.6.3 release solves my real-life use case.
Thank you again.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants