Skip to content

Serialization of custom Map implementations using dynamic codegen mode is broken (tested on latest: 0.9.22) #189

Closed
@adiosmsu

Description

@adiosmsu

There is a bug in dynamic code regarding doubling of ':' symbols. How to reproduce:

import com.google.common.collect.ImmutableMap;
import com.jsoniter.output.EncodingMode;
import com.jsoniter.output.JsonStream;

import java.math.BigDecimal;

public class TestCase {
            static void main(String[] args) {
                        JsonStream.setMode(EncodingMode.DYNAMIC_MODE);

                        final ImmutableMap<String, Object> of =
                                ImmutableMap.of("destination", "test_destination_value", "amount", new BigDecimal("0.0000101101"), "password", "test_pass");
                        final String serialized = JsonStream.serialize(of);
                        System.out.println(serialized);
            }
}

(You must have

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>

on a classpath for this to compile)

Result yielded:
{"destination":"test_destination_value","amount"::0.0000101101,"password"::"test_pass"}

As you can notice, there are double ':' symbols after the first property. I believe, that is caused by codegen code in com.jsoniter.output.CodegenImplMap. You start with processing the first property with following lines (roughly, I trimmed the code unrelated to this issue):

ctx.append("java.util.Map.Entry entry = (java.util.Map.Entry)iter.next();");
genWriteMapKey(ctx, keyType, noIndention);
CodegenImplNative.genWriteOp(ctx, "entry.getValue()", valueType, false);

So far everything is OK, but then a 'while' cycle comes which messes things up:

ctx.append("while(iter.hasNext()) {");
ctx.append("entry = (java.util.Map.Entry)iter.next();");
genWriteMapKey(ctx, keyType, noIndention);
ctx.append("stream.write(':');"); //<-----THIS IS BAD
CodegenImplNative.genWriteOp(ctx, "entry.getValue()", valueType, false);

Take note of "stream.write(':');". That is a bug because in runtime "genWriteMapKey" calls "JsonStream.writeObjectField(java.lang.Object, com.jsoniter.spi.Encoder)" which looks like:

    public final void writeObjectField(Object key, Encoder keyEncoder) throws IOException {
        keyEncoder.encode(key, this);
        if (indention > 0) {
            write((byte) ':', (byte) ' ');
        } else {
            write(':');
        }
    }

As you can see it writes another ':' to a stream. Hence, a doubling.

Hope it helps to fix this fast. For a time being we are falling back to reflection mode which works OK.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions