Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions johnzon-jsonb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@
<exclude>**/AnnotationTypeInfoTest</exclude>
</excludes>

<environmentVariables>
<LC_ALL>en_US.UTF-8</LC_ALL>
</environmentVariables>

<systemPropertyVariables>
<jimage.dir>${project.build.directory}/jimage</jimage.dir>
<signature.sigTestClasspath>${project.build.directory}/signaturedirectory/jakarta.json.bind-api.jar:${project.build.directory}/jimage/java.base:${project.build.directory}/jimage/java.rmi:${project.build.directory}/jimage/java.sql:${project.build.directory}/jimage/java.naming</signature.sigTestClasspath>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.apache.johnzon.mapper.SerializeValueFilter;
import org.apache.johnzon.mapper.access.AccessMode;
import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
import org.apache.johnzon.mapper.access.MethodAccessMode;
import org.apache.johnzon.mapper.converter.LocaleConverter;
import org.apache.johnzon.mapper.internal.AdapterKey;

Expand Down Expand Up @@ -209,24 +210,10 @@ public Jsonb build() {

final AccessMode accessMode = config.getProperty("johnzon.accessMode")
.map(this::toAccessMode)
.orElseGet(() -> new JsonbAccessMode(
.orElseGet(() -> newJsonbAccessMode(
propertyNamingStrategy, orderValue, visibilityStrategy,
!namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
builder.getAdapters(),
factory, jsonp, builderFactorySupplier, parserFactoryProvider,
config.getProperty("johnzon.accessModeDelegate")
.map(this::toAccessMode)
.orElseGet(() -> new FieldAndMethodAccessMode(true, true, false, false, true)),
// this changes in v3 of the spec so let's use this behavior which makes everyone happy by default
config.getProperty("johnzon.failOnMissingCreatorValues")
.map(this::toBool)
.orElseGet(() -> config.getProperty("jsonb.creator-parameters-required")
.map(this::toBool)
.orElse(false)),
isNillable,
config.getProperty("johnzon.supportsPrivateAccess")
.map(this::toBool)
.orElse(false)));
namingStrategyValue, factory, builderFactorySupplier, parserFactoryProvider,
isNillable));
builder.setAccessMode(accessMode);

config.getProperty("johnzon.snippetMaxLength")
Expand All @@ -235,6 +222,12 @@ public Jsonb build() {
Integer.parseInt(it.toString()))
.ifPresent(builder::setSnippetMaxLength);

config.getProperty("johnzon.version")
.map(it -> Number.class.isInstance(it)?
Number.class.cast(it).intValue() :
Integer.parseInt(it.toString()))
.ifPresent(builder::setVersion);

config.getProperty("johnzon.use-biginteger-stringadapter")
.or(() -> Optional.ofNullable(System.getProperty("johnzon.use-biginteger-stringadapter")))
.map(Object::toString).map(Boolean::parseBoolean)
Expand Down Expand Up @@ -381,6 +374,75 @@ private Integer toInt(final Object v) {
return !Integer.class.isInstance(v) ? Integer.parseInt(v.toString()) : Integer.class.cast(v);
}

private Set<String> toExcludedMethods(final Object v) {
if (Set.class.isInstance(v)) {
return Set.class.cast(v);
}
final String str = v.toString().trim();
if (str.isEmpty()) {
return Set.of();
}
final Set<String> result = new HashSet<>();
for (final String s : str.split(",")) {
result.add(s.trim());
}
return result;
}

private JsonbAccessMode newJsonbAccessMode(final PropertyNamingStrategy propertyNamingStrategy,
final String orderValue,
final PropertyVisibilityStrategy visibilityStrategy,
final Optional<Object> namingStrategyValue,
final JohnzonAdapterFactory factory,
final Supplier<JsonBuilderFactory> builderFactorySupplier,
final Supplier<JsonParserFactory> parserFactoryProvider,
final boolean isNillable) {
final AccessMode delegateMode = config.getProperty("johnzon.accessModeDelegate")
.map(this::toAccessMode)
.orElseGet(() -> new FieldAndMethodAccessMode(true, true, false, false, true));
if (MethodAccessMode.class.isInstance(delegateMode)) {
ifExcludedMethods()
.ifPresent(MethodAccessMode.class.cast(delegateMode)::setExcludedMethods);
ifSupportAllRecordAttributes()
.ifPresent(v -> MethodAccessMode.class.cast(delegateMode).setSupportAllRecordAttributes(v.booleanValue()));
} else if (FieldAndMethodAccessMode.class.isInstance(delegateMode)) {
ifExcludedMethods()
.ifPresent(FieldAndMethodAccessMode.class.cast(delegateMode)::setExcludedMethods);
ifSupportAllRecordAttributes()
.ifPresent(v -> FieldAndMethodAccessMode.class.cast(delegateMode).setSupportAllRecordAttributes(v.booleanValue()));
}
return new JsonbAccessMode(
propertyNamingStrategy, orderValue, visibilityStrategy,
!namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
builder.getAdapters(),
factory, jsonp, builderFactorySupplier, parserFactoryProvider,
delegateMode,
config.getProperty("johnzon.failOnMissingCreatorValues")
.map(this::toBool)
.orElseGet(() -> config.getProperty("jsonb.creator-parameters-required")
.map(this::toBool)
.orElse(false)),
isNillable,
config.getProperty("johnzon.supportsPrivateAccess")
.map(this::toBool)
.orElse(false),
config.getProperty("johnzon.version")
.map(it -> Number.class.isInstance(it) ?
Number.class.cast(it).intValue() :
Integer.parseInt(it.toString()))
.orElse(-1));
}

private Optional<Boolean> ifSupportAllRecordAttributes() {
return config.getProperty("johnzon.accessMode.supportAllRecordAttributes")
.map(this::toBool);
}

private Optional<Set<String>> ifExcludedMethods() {
return config.getProperty("johnzon.accessMode.excludedMethods")
.map(this::toExcludedMethods);
}

private AccessMode toAccessMode(final Object s) {
if (String.class.isInstance(s)) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.johnzon.mapper.Converter;
import org.apache.johnzon.mapper.JohnzonAny;
import org.apache.johnzon.mapper.JohnzonConverter;
import org.apache.johnzon.mapper.JohnzonIgnore;
import org.apache.johnzon.mapper.JohnzonRecord;
import org.apache.johnzon.mapper.MapperConverter;
import org.apache.johnzon.mapper.MappingGenerator;
Expand Down Expand Up @@ -156,6 +157,7 @@ protected Map<String, Writer> doFindWriters(Class<?> clazz) {
private final Types types = new Types();
private final boolean globalIsNillable;
private final boolean supportsPrivateAccess;
private final int version;

// CHECKSTYLE:OFF
public JsonbAccessMode(final PropertyNamingStrategy propertyNamingStrategy, final String orderValue,
Expand All @@ -168,6 +170,24 @@ public JsonbAccessMode(final PropertyNamingStrategy propertyNamingStrategy, fina
final boolean globalIsNillable,
final boolean supportsPrivateAccess) {
// CHECKSTYLE:ON
this(
propertyNamingStrategy, orderValue, visibilityStrategy, caseSensitive, defaultConverters, factory,
jsonProvider, builderFactory, parserFactory, delegate, failOnMissingCreatorValues, globalIsNillable,
supportsPrivateAccess, 0);
}

// CHECKSTYLE:OFF
public JsonbAccessMode(final PropertyNamingStrategy propertyNamingStrategy, final String orderValue,
final PropertyVisibilityStrategy visibilityStrategy, final boolean caseSensitive,
final Map<AdapterKey, Adapter<?, ?>> defaultConverters, final JohnzonAdapterFactory factory,
final JsonProvider jsonProvider, final Supplier<JsonBuilderFactory> builderFactory,
final Supplier<JsonParserFactory> parserFactory,
final AccessMode delegate,
final boolean failOnMissingCreatorValues,
final boolean globalIsNillable,
final boolean supportsPrivateAccess,
final int version) {
// CHECKSTYLE:ON
this.globalIsNillable = globalIsNillable;
this.naming = propertyNamingStrategy;
this.order = orderValue;
Expand All @@ -181,6 +201,7 @@ public JsonbAccessMode(final PropertyNamingStrategy propertyNamingStrategy, fina
this.parserFactory = parserFactory;
this.failOnMissingCreatorValues = failOnMissingCreatorValues;
this.supportsPrivateAccess = supportsPrivateAccess;
this.version = version;
}

@Override
Expand Down Expand Up @@ -953,6 +974,10 @@ private boolean isTransient(final DecoratedType t) {
if (t.getAnnotation(JsonbTransient.class) != null) {
return true;
}
final JohnzonIgnore johnzonIgnore = t.getAnnotation(JohnzonIgnore.class);
if (johnzonIgnore != null && johnzonIgnore.minVersion() >= 0 && version < johnzonIgnore.minVersion()) {
return true;
}
// TODO: spec requirement, this sounds wrong since you cant customize 2 kind of serializations on the same model
if (FieldAccessMode.FieldDecoratedType.class.isInstance(t)) {
final Field field = FieldAccessMode.FieldDecoratedType.class.cast(t).getField();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.johnzon.jsonb;

import org.apache.johnzon.mapper.JohnzonIgnore;
import org.apache.johnzon.mapper.JohnzonRecord;
import org.apache.johnzon.jsonb.test.JsonbRule;
import org.junit.Rule;
import org.junit.Test;

import jakarta.json.bind.annotation.JsonbTransient;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class JohnzonIgnoreJsonbTest {
@Rule
public final JsonbRule jsonb = new JsonbRule();

@Rule
public final JsonbRule jsonbWithExcludedMethods = new JsonbRule()
.withProperty("johnzon.accessMode.excludedMethods", "extra")
.withProperty("johnzon.accessMode.supportAllRecordAttributes", Boolean.TRUE);

@Rule
public final JsonbRule jsonbWithVersion0 = new JsonbRule()
.withProperty("johnzon.version", 0);

@Rule
public final JsonbRule jsonbWithVersion2 = new JsonbRule()
.withProperty("johnzon.version", 2);

@Test
public void johnzonIgnoreDefaultMinVersionIsTransient() {
final AlwaysIgnored obj = new AlwaysIgnored();
obj.visible = "yes";
obj.hidden = "no";
final String json = jsonb.toJson(obj);
assertTrue(json.contains("\"visible\""));
assertFalse(json.contains("\"hidden\""));
}

@Test
public void johnzonIgnoreWithJsonbTransientBothAreTransient() {
final BothTransient obj = new BothTransient();
obj.field1 = "a";
obj.field2 = "b";
final String json = jsonb.toJson(obj);
assertFalse(json.contains("\"field1\""));
assertFalse(json.contains("\"field2\""));
}

@Test
public void johnzonIgnoreVersionedRespectedViaJsonbConfig() {
final VersionedRecord obj = new VersionedRecord();
obj.name = "test";
obj.extra = "data";
final String json = jsonbWithExcludedMethods.toJson(obj);
assertTrue(json.contains("\"name\""));
assertFalse(json.contains("\"extra\""));
}

@Test
public void johnzonIgnoreMinVersionBelowConfiguredVersionIsNotTransient() {
final VersionedPojo obj = new VersionedPojo();
obj.visible = "yes";
obj.hidden = "no";
final String json = jsonbWithVersion2.toJson(obj);
assertTrue(json.contains("\"visible\""));
assertTrue(json.contains("\"hidden\""));
}

@Test
public void johnzonIgnoreMinVersionAboveConfiguredVersionIsTransient() {
final VersionedPojo obj = new VersionedPojo();
obj.visible = "yes";
obj.hidden = "no";
final String json = jsonbWithVersion0.toJson(obj);
assertTrue(json.contains("\"visible\""));
assertFalse(json.contains("\"hidden\""));
}

public static class AlwaysIgnored {
public String visible;

@JohnzonIgnore
public String hidden;

public String getVisible() {
return visible;
}

public void setVisible(final String visible) {
this.visible = visible;
}

public String getHidden() {
return hidden;
}

public void setHidden(final String hidden) {
this.hidden = hidden;
}
}

public static class BothTransient {
@JsonbTransient
public String field1;

@JohnzonIgnore
public String field2;

public String getField1() {
return field1;
}

public void setField1(final String field1) {
this.field1 = field1;
}

public String getField2() {
return field2;
}

public void setField2(final String field2) {
this.field2 = field2;
}
}

@JohnzonRecord
public static class VersionedRecord {
private String name;
private String extra;

public String name() {
return name;
}

public String extra() {
return extra;
}
}

public static class VersionedPojo {
private String visible;
private String hidden;

public String getVisible() {
return visible;
}

public void setVisible(final String visible) {
this.visible = visible;
}

@JohnzonIgnore(minVersion = 2)
public String getHidden() {
return hidden;
}

public void setHidden(final String hidden) {
this.hidden = hidden;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public JsonbRule withTypeAdapter(JsonbAdapter<?, ?>... jsonbAdapters) {
return this;
}

public JsonbRule withProperty(final String key, final Object value) {
config.setProperty(key, value);
return this;
}

@Override
public Statement apply(final Statement statement, final Description description) {
return new Statement() {
Expand Down
Loading
Loading