public class CustomType
{
    private long sid=0l;
    private String name="name";
    
    
    public CustomType(long sid,String name){
        this.setSid( sid );
        this.setName( name );
    }
    
    public long getSid()
    {
        return sid;
    }

    public void setSid( long sid )
    {
        this.sid = sid;
    }

    public String getName()
    {
        return name;
    }

    public void setName( String name )
    {
        this.name = name;
    } 
    
    
    public boolean equals(Object obj){
        if(obj instanceof CustomType){
            CustomType c=(CustomType)obj;
            return c.name.equals( name ) && c.sid==sid;
        }else{
            return false;
        }
    }
    
} public class CustomTypeSerializer
    extends StdSerializer<CustomType> {
  private static final long serialVersionUID = 5275226323866715671L;

  public CustomTypeSerializer() {
    super(CustomType.class, true);
  }

  @Override
  public void serialize(CustomType value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
    if(value == null) {
      jgen.writeNull();
    } else {
      jgen.writeString(value.getSid() + "[" + value.getName() + "]");
    }
  }

} public class NonReplacableKeyMap<K, V> extends LinkedHashMap<K, V> {
  @Override
  public V put(K key, V value) {
    if(containsKey(key)) {
      throw new IllegalStateException();
    }
    return super.put(key, value);
  }
} @JsonAutoDetect
public static class AutodetectDefault {
  private String id;

  public String getId() {
    return "valid";
  }

  public void setId(String id) {
    this.id = id;
  }
} @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE)
public static class AutodetectFields {
  private String id;

  public void setId(String id) {
    this.id = id;
  }

  private String getId() {
    return "not_valid";
  }
} @JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.ANY)
public static class AutodetectGetters {
  private String id;

  public void setId(String id) {
    this.id = id;
  }

  private String getId() {
    return "valid";
  }
} public static class AutodetectNotPresent {
  private String id;

  public String getId() {
    return "valid";
  }

  public void setId(String id) {
    this.id = id;
  }
} @JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class TestBackreferenceObject {

  private String id;
  @JsonBackReference
  private List<TestForwardReferenceObject> children;


  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public List<TestForwardReferenceObject> getChildren() {
    return children;
  }

  public void setChildren(List<TestForwardReferenceObject> children) {
    this.children = children;
  }

  @JsonAutoDetect(fieldVisibility = Visibility.ANY)
  public static class TestForwardReferenceObject{
    private String id;
    @JsonManagedReference
    private TestBackreferenceObject parent;

    public String getId() {
      return id;
    }

    public void setId(String id) {
      this.id = id;
    }

    public TestBackreferenceObject getParent() {
      return parent;
    }

    public void setParent(TestBackreferenceObject parent) {
      this.parent = parent;
    }
  }
} @JsonAutoDetect(fieldVisibility = Visibility.ANY)
public static class TestForwardReferenceObject{
  private String id;
  @JsonManagedReference
  private TestBackreferenceObject parent;

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public TestBackreferenceObject getParent() {
    return parent;
  }

  public void setParent(TestBackreferenceObject parent) {
    this.parent = parent;
  }
} static class ClassC extends ClassB {} public interface TestInterface {
  Date getDate();
} @JsonSerialize(include = Inclusion.NON_NULL)
public class TestNonNulls {
  private String val;
  private String otherVal;
  private TestNonNulls sub;

  public TestNonNulls(String val) {
    this.val = val;
  }

  public TestNonNulls(String val, TestNonNulls sub) {
    this.val = val;
    this.sub = sub;
  }

  public TestNonNulls() {
  }

  public String getVal() {
    return val;
  }

  public void setVal(String val) {
    this.val = val;
  }

  public String getOtherVal() {
    return otherVal;
  }

  public void setOtherVal(String otherVal) {
    this.otherVal = otherVal;
  }

  public TestNonNulls getSub() {
    return sub;
  }

  public void setSub(TestNonNulls sub) {
    this.sub = sub;
  }
} @JsonSerialize(include = Inclusion.ALWAYS)
public class TestNulls {
  private String val;
  private String otherVal;
  private TestNulls sub;

  public TestNulls(String val) {
    this.val = val;
  }

  public TestNulls(String val, TestNulls sub) {
    this.val = val;
    this.sub = sub;
  }

  public TestNulls() {
  }

  public String getVal() {
    return val;
  }

  public void setVal(String val) {
    this.val = val;
  }

  public String getOtherVal() {
    return otherVal;
  }

  public void setOtherVal(String otherVal) {
    this.otherVal = otherVal;
  }

  public TestNulls getSub() {
    return sub;
  }

  public void setSub(TestNulls sub) {
    this.sub = sub;
  }
} @JsonIgnoreProperties({"ignoredIndirect"})
public class TestObject implements TestInterface {
  public enum TestEnum {VALUE_A, VALUE_B}

  public static final String PUBLIC_FIELD = "public";
  private static final String PRIVATE_FIELD = "private";

  private TestSubobject sub;
  @JsonIgnoreProperties({"val"})
  private TestSubobject subWithIgnores;
  private String str1;
  @JsonInclude(value = JsonInclude.Include.NON_NULL)
  private String str2;
  @JsonIgnore
  private String ignoredDirect;
  private String targetObject;
  private String ignoredIndirect;
  private Date date;
  private int int1;
  private List<String> list;
  private Map<String, String> mapOfStrings;
  private Map<Integer, String> mapWithIntKeys;
  private String[] stringArray;
  private byte[] byteArray;
  private int[] intArray;
  private TestObject[] objArray;
  private List<TestSubobject> listOfObjects;
  private Map<String, TestSubobject> mapOfObjects;
  private TestEnum testEnum;
  private URL url;
  private URI uri;
  private Class cls;
  private BigDecimal bigDecimal;
  private CustomType custom;
  @JsonSerialize(using = CustomTypeSerializer.class)
  private CustomType customFieldSerializer;
  private ZonedDateTime zonedDateTime;
  @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC")
  private ZonedDateTime formattedZonedDateTime;
  @JsonProperty("totallyJsonProp")
  private String jsonProp;
  @JsonProperty
  private String jsonPropNoValue;
  private UUID uuid;
  private TestObject recursion;
  private JsonNode jsonNode;
  private String widgetName;

  public String getStr1() {
    return str1;
  }

  public void setStr1(String str1) {
    this.str1 = str1;
  }

  public String getStr2() {
    return str2;
  }

  public void setStr2(String str2) {
    this.str2 = str2;
  }

  public String getIgnoredDirect() {
    return ignoredDirect;
  }

  public void setIgnoredDirect(String ignoredDirect) {
    this.ignoredDirect = ignoredDirect;
  }

  public String getIgnoredIndirect() {
    return ignoredIndirect;
  }

  public void setIgnoredIndirect(String ignoredIndirect) {
    this.ignoredIndirect = ignoredIndirect;
  }

  public String getTargetObject() {
    return targetObject;
  }

  public void setTargetObject(String targetObject) {
    this.targetObject = targetObject;
  }

  public Date getDate() {
    return date;
  }

  public void setDate(Date date) {
    this.date = date;
  }

  public int getInt1() {
    return int1;
  }

  public void setInt1(int int1) {
    this.int1 = int1;
  }

  public TestSubobject getSub() {
    return sub;
  }

  public void setSub(TestSubobject sub) {
    this.sub = sub;
  }

  public List<String> getList() {
    return list;
  }

  public void setList(List<String> list) {
    this.list = list;
  }

  public Map<String, String> getMapOfStrings() {
    return mapOfStrings;
  }

  public void setMapOfStrings(Map<String, String> mapOfStrings) {
    this.mapOfStrings = mapOfStrings;
  }

  public Map<Integer, String> getMapWithIntKeys() {
    return mapWithIntKeys;
  }

  public void setMapWithIntKeys(Map<Integer, String> mapWithIntKeys) {
    this.mapWithIntKeys = mapWithIntKeys;
  }

  public String[] getStringArray() {
    return stringArray;
  }

  public void setStringArray(String[] stringArray) {
    this.stringArray = stringArray;
  }

  public byte[] getByteArray() {
    return byteArray;
  }

  public void setByteArray(byte[] byteArray) {
    this.byteArray = byteArray;
  }

  public int[] getIntArray() {
    return intArray;
  }

  public void setIntArray(int[] intArray) {
    this.intArray = intArray;
  }

  public TestObject[] getObjArray() {
    return objArray;
  }

  public void setObjArray(TestObject[] objArray) {
    this.objArray = objArray;
  }

  public List<TestSubobject> getListOfObjects() {
    return listOfObjects;
  }

  public void setListOfObjects(List<TestSubobject> listOfObjects) {
    this.listOfObjects = listOfObjects;
  }

  public Map<String, TestSubobject> getMapOfObjects() {
    return mapOfObjects;
  }

  public void setMapOfObjects(Map<String, TestSubobject> mapOfObjects) {
    this.mapOfObjects = mapOfObjects;
  }

  public TestEnum getTestEnum() {
    return testEnum;
  }

  public void setTestEnum(TestEnum testEnum) {
    this.testEnum = testEnum;
  }

  public URL getUrl() {
    return url;
  }

  public void setUrl(URL url) {
    this.url = url;
  }

  public URI getUri() {
    return uri;
  }

  public void setUri(URI uri) {
    this.uri = uri;
  }

  public Class getCls() {
    return cls;
  }

  public void setCls(Class cls) {
    this.cls = cls;
  }

  public TestSubobject getSubWithIgnores() {
    return subWithIgnores;
  }

  public void setSubWithIgnores(TestSubobject subWithIgnores) {
    this.subWithIgnores = subWithIgnores;
  }

  public BigDecimal getBigDecimal() {
    return bigDecimal;
  }

  public void setBigDecimal(BigDecimal bigDecimal) {
    this.bigDecimal = bigDecimal;
  }

  public CustomType getCustom() {
    return custom;
  }

  public void setCustom(CustomType custom) {
    this.custom = custom;
  }

  public ZonedDateTime getZonedDateTime() {
    return zonedDateTime;
  }

  public void setZonedDateTime(ZonedDateTime zonedDateTime) {
    this.zonedDateTime = zonedDateTime;
  }

  public ZonedDateTime getFormattedZonedDateTime() {
    return formattedZonedDateTime;
  }

  public void setFormattedZonedDateTime(ZonedDateTime formattedZonedDateTime) {
    this.formattedZonedDateTime = formattedZonedDateTime;
  }

  public CustomType getCustomFieldSerializer() {
    return customFieldSerializer;
  }

  public void setCustomFieldSerializer(CustomType customFieldSerializer) {
    this.customFieldSerializer = customFieldSerializer;
  }

  public String getJsonProp() {
    return jsonProp;
  }

  public void setJsonProp(String jsonProp) {
    this.jsonProp = jsonProp;
  }

  public String getJsonPropNoValue() {
    return jsonPropNoValue;
  }

  public void setJsonPropNoValue(String jsonPropNoValue) {
    this.jsonPropNoValue = jsonPropNoValue;
  }

  public UUID getUuid() {
    return uuid;
  }

  public void setUuid(UUID uuid) {
    this.uuid = uuid;
  }

  public TestObject getRecursion() {
    return recursion;
  }

  public void setRecursion(TestObject recursion) {
    this.recursion = recursion;
  }

  public JsonNode getJsonNode() {
    return jsonNode;
  }

  public void setJsonNode(JsonNode jsonNode) {
    this.jsonNode = jsonNode;
  }

  public String getStaticValue() {
    return "TEST";
  }

  @JsonIgnore
  public String getIgnoredValue() {
    return "not_valid";
  }
  
  public String getWidgetName() {
    return widgetName;
  }

  public void setWidgetName(String widgetName) {
    this.widgetName = widgetName;
  }
} public enum TestEnum {VALUE_A, VALUE_B} public class TestSubobject {
  private String val;
  private TestSubobject sub;
  private List<TestSubobject> subs;

  public TestSubobject(String val) {
    this.val = val;
  }

  public TestSubobject(String val, TestSubobject sub) {
    this.val = val;
    this.sub = sub;
  }

  public TestSubobject() {
  }

  public String getVal() {
    return val;
  }

  public void setVal(String val) {
    this.val = val;
  }

  public TestSubobject getSub() {
    return sub;
  }

  public void setSub(TestSubobject sub) {
    this.sub = sub;
  }

  public List<TestSubobject> getSubs() {
    return subs;
  }

  public void setSubs(List<TestSubobject> subs) {
    this.subs = subs;
  }
} public interface TestSuperinterface {

  default String getId() {
    return "ID";
  }

  public static interface TestChildInterface extends TestSuperinterface {
    default String getName() {
      return "NAME";
    }
  }

  public static class TestInterfaceObject implements TestChildInterface {
    private String description;

    public String getDescription() {
      return description;
    }

    public void setDescription(String description) {
      this.description = description;
    }
  }
} public static interface TestChildInterface extends TestSuperinterface {
  default String getName() {
    return "NAME";
  }
} public static class TestInterfaceObject implements TestChildInterface {
  private String description;

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }
} public static Match match() {
  return new Match();
}
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
import com.google.common.primitives.Ints;
import com.monitorjbl.json.model.CustomType;
import com.monitorjbl.json.model.CustomTypeSerializer;
import com.monitorjbl.json.model.NonReplacableKeyMap;
import com.monitorjbl.json.model.TestAutodetect.AutodetectDefault;
import com.monitorjbl.json.model.TestAutodetect.AutodetectFields;
import com.monitorjbl.json.model.TestAutodetect.AutodetectGetters;
import com.monitorjbl.json.model.TestAutodetect.AutodetectNotPresent;
import com.monitorjbl.json.model.TestBackreferenceObject;
import com.monitorjbl.json.model.TestBackreferenceObject.TestForwardReferenceObject;
import com.monitorjbl.json.model.TestChildObject;
import com.monitorjbl.json.model.TestDuplicateKeys.ClassC;
import com.monitorjbl.json.model.TestInterface;
import com.monitorjbl.json.model.TestNonNulls;
import com.monitorjbl.json.model.TestNulls;
import com.monitorjbl.json.model.TestObject;
import com.monitorjbl.json.model.TestObject.TestEnum;
import com.monitorjbl.json.model.TestSubobject;
import com.monitorjbl.json.model.TestSuperinterface;
import com.monitorjbl.json.model.TestSuperinterface.TestChildInterface;
import com.monitorjbl.json.model.TestSuperinterface.TestInterfaceObject;
import com.monitorjbl.json.model.TestUnrelatedObject;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
import static com.monitorjbl.json.Match.match;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
            .exclude("*")));
    doTest.accept(sut.readValue(serialized, NonReplacableKeyMap.class));
  }

  @Test
  public void testFieldTransform() throws Exception {
    TestObject ref = new TestObject();
    ref.setInt1(1);
    ref.setStr1("sdfdsf");

    sut = new ObjectMapper().registerModule(new JsonViewModule(serializer));

    String serialized = sut.writeValueAsString(JsonView.with(ref)
        .onClass(TestObject.class, match()
            .exclude("*")
            .include("str1", "str2")
            .transform("str1", (TestObject t, String f) -> f.toUpperCase())
            .transform("str2", (TestObject t, String f) -> t.getStr1())));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertNull(obj.get("int1"));
    assertNotNull(obj.get("str1"));
    assertEquals(ref.getStr1().toUpperCase(), obj.get("str1"));
    assertNotNull(obj.get("str2"));
    assertEquals(ref.getStr1(), obj.get("str2"));
  }

  @Test
  public void testMethodGetters() throws Exception {
    TestObject ref = new TestObject();

    String serialized = sut.writeValueAsString(JsonView.with(ref));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertEquals("TEST", obj.get("staticValue"));
  }

  @Test
  public void testMethodGetters_ignored() throws Exception {
    TestObject ref = new TestObject();

    String serialized = sut.writeValueAsString(JsonView.with(ref));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertFalse(obj.containsKey("ignoredValue"));
  }

  @Test
  public void testMethodGetters_combinesAnnotations() throws Exception {
    TestObject ref = new TestObject();

    String serialized = sut.writeValueAsString(JsonView.with(ref));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertFalse(obj.containsKey("ignoredDirect"));
  }

  @Test
  public void testAutodetect_notPresent() throws Exception {
    AutodetectNotPresent ref = new AutodetectNotPresent();
    ref.setId("test");

    String serialized = sut.writeValueAsString(JsonView.with(ref));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertEquals("valid", obj.get("id"));
  }

  @Test
  public void testAutodetect_default() throws Exception {
    AutodetectDefault ref = new AutodetectDefault();
    ref.setId("test");

    String serialized = sut.writeValueAsString(JsonView.with(ref));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertEquals("valid", obj.get("id"));
  }

  @Test
  public void testAutodetect_fieldsOnly() throws Exception {
    AutodetectFields ref = new AutodetectFields();
    ref.setId("test");

    String serialized = sut.writeValueAsString(JsonView.with(ref));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertEquals("test", obj.get("id"));
  }

  @Test
  public void testAutodetect_methodsOnly() throws Exception {
    AutodetectGetters ref = new AutodetectGetters();
    ref.setId("test");

    String serialized = sut.writeValueAsString(JsonView.with(ref));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertEquals("valid", obj.get("id"));
  }

  @Test
  public void testSuperinterfaces() throws Exception {
    TestInterfaceObject ref = new TestInterfaceObject();
    ref.setDescription("description");

    String serialized = sut.writeValueAsString(JsonView.with(ref));
    Map<String, Object> obj = sut.readValue(serialized, NonReplacableKeyMap.class);

    assertEquals("ID", obj.get("id"));
    assertEquals("NAME", obj.get("name"));
    assertEquals("description", obj.get("description"));
  }

  @Test
  public void testSuperinterfaces_matchers() throws Exception {
    TestInterfaceObject ref = new TestInterfaceObject();
    ref.setDescription("description");

    String serialized = sut.writeValueAsString(JsonView.with(ref)
        .onClass(TestSuperinterface.class, match()