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
2 changes: 1 addition & 1 deletion jsonschema2pojo-ant/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>jsonschema2pojo</artifactId>
<groupId>org.jsonschema2pojo</groupId>
<version>1.2.3-SNAPSHOT</version>
<version>1.3.0-SNAPSHOT</version>
</parent>

<artifactId>jsonschema2pojo-ant</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion jsonschema2pojo-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>jsonschema2pojo</artifactId>
<groupId>org.jsonschema2pojo</groupId>
<version>1.2.3-SNAPSHOT</version>
<version>1.3.0-SNAPSHOT</version>
</parent>

<artifactId>jsonschema2pojo-cli</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion jsonschema2pojo-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>jsonschema2pojo</artifactId>
<groupId>org.jsonschema2pojo</groupId>
<version>1.2.3-SNAPSHOT</version>
<version>1.3.0-SNAPSHOT</version>
</parent>

<artifactId>jsonschema2pojo-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class Schema {
private final JsonNode content;
private final Schema parent;
private JType javaType;
private boolean nullable = false;

public Schema(URI id, JsonNode content, Schema parent) {
this.id = id;
Expand Down Expand Up @@ -75,4 +76,11 @@ public boolean isGenerated() {
return javaType != null;
}

public boolean isNullable() {
return nullable;
}

public void setNullable(boolean nullable) {
this.nullable = nullable;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ public SchemaStore(ContentResolver contentResolver, RuleLogger logger) {
* contents of the given ID as a URL. If a schema with the given ID is
* already known, then a reference to the original schema will be returned.
*
* @param id
* the id of the schema being created
* @param id the id of the schema being created
* @param refFragmentPathDelimiters A string containing any characters
* that should act as path delimiters when resolving $ref fragments.
* @param parent
* @return a schema object containing the contents of the given path
*/
public synchronized Schema create(URI id, String refFragmentPathDelimiters) {
public synchronized Schema create(URI id, String refFragmentPathDelimiters, Schema parent) {

URI normalizedId = id.normalize();

Expand All @@ -64,13 +64,13 @@ public synchronized Schema create(URI id, String refFragmentPathDelimiters) {
if (!schemas.containsKey(baseId)) {
logger.debug("Reading schema: " + baseId);
final JsonNode baseContent = contentResolver.resolve(baseId);
schemas.put(baseId, new Schema(baseId, baseContent, null));
schemas.put(baseId, new Schema(baseId, baseContent, parent));
}

final Schema baseSchema = schemas.get(baseId);
if (normalizedId.toString().contains("#")) {
JsonNode childContent = fragmentResolver.resolve(baseSchema.getContent(), '#' + id.getFragment(), refFragmentPathDelimiters);
schemas.put(normalizedId, new Schema(normalizedId, childContent, baseSchema));
schemas.put(normalizedId, new Schema(normalizedId, childContent, parent != null ? parent : baseSchema));
}
}

Expand Down Expand Up @@ -141,8 +141,29 @@ public Schema create(Schema parent, String path, String refFragmentPathDelimiter
}
}

return create(id, refFragmentPathDelimiters);
return create(id, refFragmentPathDelimiters, parent);
}

/**
* Creates or looks up a new property schema using the given schema as a parent.
*
* @param parentSchema parent schema to start the resolution with
* @param schemaPropertyName name of the field to create a schema for
* @param refFragmentPathDelimiters A string containing any characters
* that should act as path delimiters when resolving $ref fragments.
* @return a schema object for the property
*/
public Schema createPropertySchema(Schema parentSchema, String schemaPropertyName, String refFragmentPathDelimiters) {
String pathToProperty;
if (parentSchema.getId() == null || parentSchema.getId().getFragment() == null) {
pathToProperty = "#/properties/" + JsonPointerUtils.encodeReferenceToken(schemaPropertyName);
} else {
pathToProperty =
"#" + parentSchema.getId().getFragment() + "/properties/" +
JsonPointerUtils.encodeReferenceToken(schemaPropertyName);
}

return create(parentSchema, pathToProperty, refFragmentPathDelimiters);
}

protected boolean selfReferenceWithoutParentFile(Schema parent, String path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public JDocCommentable apply(String nodeName, JsonNode node, JsonNode parent, JD

// Since NotRequiredRule is executed for all fields that do not have "required" present,
// we need to recognize whether the field is part of the RequiredArrayRule.
JsonNode requiredArray = schema.getContent().get("required");
JsonNode requiredArray = schema.getParent().getContent().get("required");

if (requiredArray != null) {
for (Iterator<JsonNode> iterator = requiredArray.elements(); iterator.hasNext(); ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright © 2010-2020 Nokia
*
* <p>
* Licensed 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
Expand All @@ -17,7 +17,6 @@
package org.jsonschema2pojo.rules;

import org.jsonschema2pojo.GenerationConfig;
import org.jsonschema2pojo.JsonPointerUtils;
import org.jsonschema2pojo.Schema;

import com.fasterxml.jackson.databind.JsonNode;
Expand Down Expand Up @@ -74,14 +73,11 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef
propertyName = ruleFactory.getNameHelper().getPropertyName(nodeName, node);
}

String pathToProperty;
if (schema.getId() == null || schema.getId().getFragment() == null) {
pathToProperty = "#/properties/" + JsonPointerUtils.encodeReferenceToken(nodeName);
} else {
pathToProperty = "#" + schema.getId().getFragment() + "/properties/" + JsonPointerUtils.encodeReferenceToken(nodeName);
}
Schema propertySchema = ruleFactory.getSchemaStore().createPropertySchema(
schema,
nodeName,
ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters());

Schema propertySchema = ruleFactory.getSchemaStore().create(schema, pathToProperty, ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters());
JType propertyType = ruleFactory.getSchemaRule().apply(nodeName, node, parent, jclass, propertySchema);
propertySchema.setJavaTypeIfEmpty(propertyType);

Expand All @@ -93,44 +89,47 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef
int accessModifier = isIncludeGetters || isIncludeSetters ? JMod.PRIVATE : JMod.PUBLIC;
JFieldVar field = jclass.field(accessModifier, propertyType, propertyName);

propertyAnnotations(nodeName, node, schema, field);
propertyAnnotations(nodeName, node, propertySchema, field);

formatAnnotation(field, jclass, node);

ruleFactory.getAnnotator().propertyField(field, jclass, nodeName, node);

if (isIncludeGetters) {
JMethod getter = addGetter(jclass, field, nodeName, node, isRequired(nodeName, node, schema), useOptional(nodeName, node, schema));
JMethod getter = addGetter(jclass, field, nodeName, node,
isRequired(nodeName, node, propertySchema),
useOptional(nodeName, node, propertySchema));

ruleFactory.getAnnotator().propertyGetter(getter, jclass, nodeName);
propertyAnnotations(nodeName, node, schema, getter);
propertyAnnotations(nodeName, node, propertySchema, getter);
}

if (isIncludeSetters) {
JMethod setter = addSetter(jclass, field, nodeName, node);
ruleFactory.getAnnotator().propertySetter(setter, jclass, nodeName);
propertyAnnotations(nodeName, node, schema, setter);
propertyAnnotations(nodeName, node, propertySchema, setter);
}

if (ruleFactory.getGenerationConfig().isGenerateBuilders()) {
addBuilderMethod(jclass, field, nodeName, node);
}

if (node.has("pattern")) {
ruleFactory.getPatternRule().apply(nodeName, node.get("pattern"), node, field, schema);
ruleFactory.getPatternRule().apply(nodeName, node.get("pattern"), node, field, propertySchema);
}

ruleFactory.getDefaultRule().apply(nodeName, node.get("default"), node, field, schema);
ruleFactory.getDefaultRule().apply(nodeName, node.get("default"), node, field, propertySchema);

ruleFactory.getMinimumMaximumRule().apply(nodeName, node, parent, field, schema);
ruleFactory.getMinimumMaximumRule().apply(nodeName, node, parent, field, propertySchema);

ruleFactory.getMinItemsMaxItemsRule().apply(nodeName, node, parent, field, schema);
ruleFactory.getMinItemsMaxItemsRule().apply(nodeName, node, parent, field, propertySchema);

ruleFactory.getMinLengthMaxLengthRule().apply(nodeName, node, parent, field, schema);
ruleFactory.getMinLengthMaxLengthRule().apply(nodeName, node, parent, field, propertySchema);

ruleFactory.getDigitsRule().apply(nodeName, node, parent, field, schema);
ruleFactory.getDigitsRule().apply(nodeName, node, parent, field, propertySchema);

if (isObject(node) || isArray(node)) {
ruleFactory.getValidRule().apply(nodeName, node, parent, field, schema);
ruleFactory.getValidRule().apply(nodeName, node, parent, field, propertySchema);
}

return jclass;
Expand Down Expand Up @@ -268,7 +267,7 @@ private JMethod addSetter(JDefinedClass c, JFieldVar field, String jsonPropertyN

private JMethod addBuilderMethod(JDefinedClass c, JFieldVar field, String jsonPropertyName, JsonNode node) {
JMethod result = null;
if(ruleFactory.getGenerationConfig().isUseInnerClassBuilders()) {
if (ruleFactory.getGenerationConfig().isUseInnerClassBuilders()) {
result = addInnerBuilderMethod(c, field, jsonPropertyName, node);
} else {
result = addLegacyBuilder(c, field, jsonPropertyName, node);
Expand All @@ -287,7 +286,7 @@ private JMethod addLegacyBuilder(JDefinedClass c, JFieldVar field, String jsonPr
return builder;
}

private JMethod addInnerBuilderMethod(JDefinedClass c, JFieldVar field, String jsonPropertyName, JsonNode node) {
private JMethod addInnerBuilderMethod(JDefinedClass c, JFieldVar field, String jsonPropertyName, JsonNode node) {
JDefinedClass builderClass = ruleFactory.getReflectionHelper().getBaseBuilderClass(c);

JMethod builderMethod = builderClass.method(JMod.PUBLIC, builderClass.narrow(builderClass.typeParams()), getBuilderName(jsonPropertyName, node));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,18 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef
continue;
}

Schema propertySchema = ruleFactory.getSchemaStore().createPropertySchema(
schema,
requiredArrayItem,
ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters());

addJavaDoc(field);

if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()) {
if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() && !propertySchema.isNullable()) {
addNotNullAnnotation(field);
}

if (ruleFactory.getGenerationConfig().isIncludeJsr305Annotations()) {
if (ruleFactory.getGenerationConfig().isIncludeJsr305Annotations() && !propertySchema.isNullable()) {
addNonnullAnnotation(field);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ protected RequiredRule(RuleFactory ruleFactory) {
@Override
public JDocCommentable apply(String nodeName, JsonNode node, JsonNode parent, JDocCommentable generatableType, Schema schema) {

if (node.asBoolean()) {
if (node.asBoolean() && !schema.isNullable()) {
generatableType.javadoc().append("\n(Required)");

if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.jsonschema2pojo.Jsonschema2Pojo;
import org.jsonschema2pojo.Schema;
import org.jsonschema2pojo.exception.GenerationException;
Expand Down Expand Up @@ -76,6 +81,12 @@ public JType apply(String nodeName, JsonNode schemaNode, JsonNode parent, JClass
return apply(nameFromRef != null ? nameFromRef : nodeName, schemaNode, parent, generatableType, schema);
}

// Add this block to handle optional type (anyOf with null type)
JType nullableType = resolveNullableType(nodeName, schemaNode, parent, generatableType, schema);
if (nullableType != null) {
return nullableType;
}

JType javaType;
if (schemaNode.has("enum")) {
javaType = ruleFactory.getEnumRule().apply(nodeName, schemaNode, parent, generatableType, schema);
Expand All @@ -87,6 +98,54 @@ public JType apply(String nodeName, JsonNode schemaNode, JsonNode parent, JClass
return javaType;
}

private JType resolveNullableType(String nodeName, JsonNode node, JsonNode parent, JClassContainer jClassContainer, Schema schema) {
if (!node.has("anyOf") || !node.get("anyOf").isArray()) {
return null;
}

ArrayNode anyOfTypes = (ArrayNode) node.get("anyOf");

List<JsonNode> nonNullTypes = StreamSupport.stream(anyOfTypes.spliterator(), false)
.filter(typeOption -> !isNullType(typeOption))
.collect(Collectors.toList());

boolean hasNullType = anyOfTypes.size() != nonNullTypes.size();

if (hasNullType && nonNullTypes.size() == 1) {
JsonNode nonNullTypeNode = nonNullTypes.get(0).deepCopy();

ObjectNode nonNullObjectNode = (ObjectNode) nonNullTypeNode;
ObjectNode originalNode = (ObjectNode) node;

// Copy all fields from the non-null type node to the original node
nonNullObjectNode.fields().forEachRemaining(
entry -> originalNode.set(entry.getKey(), entry.getValue())
);

// set the property to nullable
schema.setNullable(true);

// Remove "anyOf" from the original node to avoid conflicts, we don't need it anymore
originalNode.remove("anyOf");

// resolve type normally
JType resolvedType = apply(nodeName, originalNode, parent, jClassContainer, schema);

// to make sure we always return a nullable type
return resolvedType.boxify();
}

return null;
}

/**
* Checks if the given schema node represents a null type.
*/
private boolean isNullType(JsonNode node) {
return node.has("type") &&
"null".equals(node.get("type").asText());
}

private String nameFromRef(String ref) {

if ("#".equals(ref)) {
Expand Down
Loading