Skip to content

Commit a10a93a

Browse files
committed
Ensure outermost document is used when resolving refs within a string (no file)
Closes #1237
1 parent e484204 commit a10a93a

File tree

5 files changed

+130
-33
lines changed

5 files changed

+130
-33
lines changed

jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Schema.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,15 @@ public JsonNode getContent() {
6262
public Schema getParent() {
6363
return parent;
6464
}
65-
65+
66+
public Schema getGrandParent() {
67+
if (parent == this) {
68+
return this;
69+
} else {
70+
return this.parent.getGrandParent();
71+
}
72+
}
73+
6674
public boolean isGenerated() {
6775
return javaType != null;
6876
}

jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/SchemaStore.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public SchemaStore(ContentResolver contentResolver) {
4444
* Create or look up a new schema which has the given ID and read the
4545
* contents of the given ID as a URL. If a schema with the given ID is
4646
* already known, then a reference to the original schema will be returned.
47-
*
47+
*
4848
* @param id
4949
* the id of the schema being created
5050
* @param refFragmentPathDelimiters A string containing any characters
@@ -82,7 +82,7 @@ protected URI removeFragment(URI id) {
8282
* path as a relative reference. If a schema with the given parent and
8383
* relative path is already known, then a reference to the original schema
8484
* will be returned.
85-
*
85+
*
8686
* @param parent
8787
* the schema which is the parent of the schema to be created.
8888
* @param path
@@ -126,8 +126,8 @@ public Schema create(Schema parent, String path, String refFragmentPathDelimiter
126126
}
127127

128128
if (selfReferenceWithoutParentFile(parent, path) || substringBefore(stringId, "#").isEmpty()) {
129-
JsonNode parentContent = parent.getParent().getContent();
130-
Schema schema = new Schema(id, fragmentResolver.resolve(parentContent, path, refFragmentPathDelimiters), parent.getParent());
129+
JsonNode parentContent = parent.getGrandParent().getContent();
130+
Schema schema = new Schema(id, fragmentResolver.resolve(parentContent, path, refFragmentPathDelimiters), parent.getGrandParent());
131131
schemas.put(id, schema);
132132
return schema;
133133
}

jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/ref/FragmentRefIT.java

+72-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import static org.hamcrest.Matchers.*;
2121

2222
import java.io.IOException;
23+
import java.lang.reflect.ParameterizedType;
24+
import java.lang.reflect.Type;
2325

2426
import org.jsonschema2pojo.Schema;
2527
import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule;
@@ -40,7 +42,7 @@ public class FragmentRefIT {
4042
private static Class<?> fragmentRefsClass;
4143

4244
@BeforeClass
43-
public static void generateAndCompileEnum() throws ClassNotFoundException {
45+
public static void generateAndCompile() throws ClassNotFoundException {
4446

4547
ClassLoader fragmentRefsClassLoader = classSchemaRule.generateAndCompile("/schema/ref/fragmentRefs.json", "com.example");
4648

@@ -113,4 +115,73 @@ public void refToInnerFragmentThatHasRefToOuterFragmentWithoutParentFile() throw
113115
new RuleFactory().getSchemaRule().apply("Example", schema, null, p, new Schema(null, schema, null));
114116
}
115117

118+
@Test
119+
public void refToInnerFragmentThatHasRefToAnotherFragmentWithoutParentFile() throws IOException {
120+
JCodeModel codeModel = new JCodeModel();
121+
JsonNode schema = new ObjectMapper().readTree("{\n"
122+
+ " \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n"
123+
+ " \"title\": \"Inbox Item Datalake DTO\",\n"
124+
+ " \"definitions\": {\n"
125+
+ " \"PropertyA\": {\n"
126+
+ " \"type\": \"object\",\n"
127+
+ " \"properties\": {\n"
128+
+ " \"value\": {\n"
129+
+ " \"type\": \"string\"\n"
130+
+ " }\n"
131+
+ " }\n"
132+
+ " },\n"
133+
+ " \"PropertyB\": {\n"
134+
+ " \"type\": \"object\",\n"
135+
+ " \"properties\": {\n"
136+
+ " \"data\": {\n"
137+
+ " \"type\": \"array\",\n"
138+
+ " \"items\": {\n"
139+
+ " \"$ref\": \"#/definitions/PropertyA\"\n"
140+
+ " },\n"
141+
+ " \"default\": []\n"
142+
+ " }\n"
143+
+ " }\n"
144+
+ " }\n"
145+
+ " },\n"
146+
+ " \"properties\": {\n"
147+
+ " \"FinalProperty\": {\n"
148+
+ " \"type\": \"array\",\n"
149+
+ " \"items\": {\n"
150+
+ " \"$ref\": \"#/definitions/PropertyB\"\n"
151+
+ " },\n"
152+
+ " \"default\": []\n"
153+
+ " }\n"
154+
+ " }\n"
155+
+ "}");
156+
157+
JPackage p = codeModel._package("com.example");
158+
new RuleFactory().getSchemaRule().apply("Example", schema, null, p, new Schema(null, schema, null));
159+
}
160+
161+
162+
@Test
163+
public void refToInnerFragmentThatHasRefToAnotherFragment() throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException {
164+
final ClassLoader fragmentRefsClassLoader = classSchemaRule.generateAndCompile("/schema/ref/refToRefToDefinitions.json", "com.example");
165+
166+
final Class<?> finalPropertyClass = fragmentRefsClassLoader.loadClass("com.example.RefToRefToDefinitions");
167+
168+
Class<?> finalPropertyType = finalPropertyClass.getMethod("getFinalProperty").getReturnType();
169+
assertThat(finalPropertyType.getName(), is("java.util.List"));
170+
171+
Type finalPropertyItemType = ((ParameterizedType)finalPropertyClass.getMethod("getFinalProperty").getGenericReturnType()).getActualTypeArguments()[0];
172+
assertThat(finalPropertyItemType.getTypeName(), is("com.example.PropertyB"));
173+
174+
final Class<?> propertyBClass = fragmentRefsClassLoader.loadClass("com.example.PropertyB");
175+
176+
Class<?> dataType = propertyBClass.getMethod("getData").getReturnType();
177+
assertThat(dataType.getName(), is("java.util.List"));
178+
179+
Type dataItemType = ((ParameterizedType)propertyBClass.getMethod("getData").getGenericReturnType()).getActualTypeArguments()[0];
180+
assertThat(dataItemType.getTypeName(), is("com.example.PropertyA"));
181+
182+
final Class<?> propertyAClass = fragmentRefsClassLoader.loadClass("com.example.PropertyA");
183+
184+
Class<?> valueType = propertyAClass.getMethod("getValue").getReturnType();
185+
assertThat(valueType.getName(), is("java.lang.String"));
186+
}
116187
}

jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/util/Jsonschema2PojoRule.java

+10-27
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,11 @@ public class Jsonschema2PojoRule implements TestRule {
5555
private boolean captureDiagnostics = false;
5656
private boolean sourceDirInitialized = false;
5757
private boolean classesDirInitialized = false;
58-
private ClassLoader classLoader;
5958
private List<Diagnostic<? extends JavaFileObject>> diagnostics;
6059

6160
public Jsonschema2PojoRule captureDiagnostics() {
62-
this.captureDiagnostics = true;
63-
return this;
61+
this.captureDiagnostics = true;
62+
return this;
6463
}
6564

6665
/**
@@ -85,20 +84,9 @@ public File getCompileDir() {
8584
return compileDir;
8685
}
8786

88-
/**
89-
* Returns the class loader for compiled classes. Only defined after calling
90-
* a compile method.
91-
*
92-
* @return The class loader for compiled classes.
93-
*/
94-
public ClassLoader getClassLoader() {
95-
checkActive();
96-
return classLoader;
97-
}
98-
9987
public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() {
100-
checkActive();
101-
return diagnostics;
88+
checkActive();
89+
return diagnostics;
10290
}
10391

10492
@Override
@@ -119,7 +107,6 @@ public void evaluate() throws Throwable {
119107
} finally {
120108
generateDir = null;
121109
compileDir = null;
122-
classLoader = null;
123110
sourceDirInitialized = false;
124111
classesDirInitialized = false;
125112
captureDiagnostics = captureDiagnosticsStart;
@@ -156,16 +143,12 @@ public ClassLoader compile(List<File> classpath) {
156143
}
157144

158145
public ClassLoader compile(List<File> classpath, Map<String, Object> config) {
159-
return compile(systemJavaCompiler(), null, classpath, config);
146+
return compile(systemJavaCompiler(), null, classpath, config);
160147
}
161148

162149
public ClassLoader compile(JavaCompiler compiler, Writer out, List<File> classpath, Map<String, Object> config) {
163-
if (classLoader != null) {
164-
throw new IllegalStateException("cannot recompile sources");
165-
}
166150
DiagnosticListener<JavaFileObject> diagnosticListener = captureDiagnostics ? new CapturingDiagnosticListener() : null;
167-
classLoader = CodeGenerationHelper.compile(compiler, out, getGenerateDir(), getCompileDir(), classpath, config, diagnosticListener);
168-
return classLoader;
151+
return CodeGenerationHelper.compile(compiler, out, getGenerateDir(), getCompileDir(), classpath, config, diagnosticListener);
169152
}
170153

171154
public ClassLoader generateAndCompile(String schema, String targetPackage, Map<String, Object> configValues) {
@@ -199,10 +182,10 @@ private void checkActive() {
199182
}
200183

201184
class CapturingDiagnosticListener implements DiagnosticListener<JavaFileObject> {
202-
@Override
203-
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
204-
diagnostics.add(diagnostic);
205-
}
185+
@Override
186+
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
187+
diagnostics.add(diagnostic);
188+
}
206189
}
207190

208191
private static List<File> emptyClasspath() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Inbox Item Datalake DTO",
4+
"definitions": {
5+
"PropertyA": {
6+
"type": "object",
7+
"properties": {
8+
"value": {
9+
"type": "string"
10+
}
11+
}
12+
},
13+
"PropertyB": {
14+
"type": "object",
15+
"properties": {
16+
"data": {
17+
"type": "array",
18+
"items": {
19+
"$ref": "#/definitions/PropertyA"
20+
},
21+
"default": []
22+
}
23+
}
24+
}
25+
},
26+
"properties": {
27+
"FinalProperty": {
28+
"type": "array",
29+
"items": {
30+
"$ref": "#/definitions/PropertyB"
31+
},
32+
"default": []
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)