boonproject / boon

Simple opinionated Java for the novice to expert level Java Programmer. Low Ceremony. High Productivity.
http://richardhightower.github.io/site/Boon/Welcome.html
Apache License 2.0
520 stars 102 forks source link

Extra ',' in generated JSON if all attributes are filtered #381

Open creinig opened 6 years ago

creinig commented 6 years ago

Scenario:

This causes InstanceSerializerImpl#serializeSubtypeInstance() to insert a "class" attribute into the JSON stream for InnerImpl, followed by a comma. Since InnerImpl does contain fields, that comma is not removed. But since all fields are filtered, the comma is not followed by another field either. => The generated JSON is invalid: {"inner":{"class":"com.iisy.solvatio.common.util.BoonIssueTest$InnerImpl",}}

Problematic code location: https://github.com/boonproject/boon/blob/9bc6870dbe5dd58c45c18d8edb493e8efc089463/boon/src/main/java/org/boon/json/serializers/impl/InstanceSerializerImpl.java#L93

Unit test to reproduce the problem:

package com.iisy.solvatio.common.util;

import static org.junit.Assert.assertFalse;

import org.boon.core.reflection.fields.FieldAccess;
import org.boon.json.JsonSerializer;
import org.boon.json.JsonSerializerFactory;
import org.boon.json.serializers.FieldFilter;
import org.boon.primitive.CharBuf;
import org.junit.Test;

public class BoonIssueTest {

    public static class Outer {
        private Inner inner;

        public Inner getInner() {
            return inner;
        }

        public void setInner(Inner inner) {
            this.inner = inner;
        }
    }

    public static interface Inner {

    }

    public static class InnerImpl implements Inner {
        private String implementationDetail = "Psst";

        public String getImplementationDetail() {
            return implementationDetail;
        }

        public void setImplementationDetail(String implementationDetail) {
            this.implementationDetail = implementationDetail;
        }

    }

    public class InternalsFilter implements FieldFilter {
        @Override
        public boolean include(final Object parent, final FieldAccess fieldAccess) {
            if (fieldAccess.name().equals("implementationDetail")) {
                return false;
            }
            return true;
        }

    }

    @Test
    public void emptySubtype() {
        Outer obj = new Outer();
        obj.setInner(new InnerImpl());
        JsonSerializerFactory factory = new JsonSerializerFactory().addFilter(new InternalsFilter());
        JsonSerializer serializer = factory.create();

        CharBuf serialized = serializer.serialize(obj);
        System.out.println(serialized);
        assertFalse(serialized.toString().contains(",}"));
    }

}