Skip to content

Commit

Permalink
Encode property names when building path to property (#1465)
Browse files Browse the repository at this point in the history
* Encode property names when building path to property

When creating a fragment with JSON Pointer path, we now encode property
names using the rules from RFC 6901. This means that the property name
can be treated as a single token, even if it contains special characters
like # or /.

Closes #1402
  • Loading branch information
joelittlejohn authored Jan 25, 2023
1 parent 82a1352 commit 9315b7b
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ private JsonNode resolve(JsonNode tree, List<String> path) {
}
}

if (tree.has(part)) {
return resolve(tree.get(part), path);
String decodedPart = JsonPointerUtils.decodeReferenceToken(part);
if (tree.has(decodedPart)) {
return resolve(tree.get(decodedPart), path);
} else {
throw new IllegalArgumentException("Path not present: " + part);
throw new IllegalArgumentException("Path not present: " + decodedPart);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright © 2010-2020 Nokia
*
* 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
*
* 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.jsonschema2pojo;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Collections;

public class JsonPointerUtils {

private static Map<String,String> SUBSTITUTIONS = new LinkedHashMap<String, String>() {{
put("~", "~0");
put("/", "~1");
put("#", "~2");
put(".", "~3");
}};

public static String encodeReferenceToken(final String s) {
String encoded = s;
for (Map.Entry<String,String> sub : SUBSTITUTIONS.entrySet()) {
encoded = encoded.replace(sub.getKey(), sub.getValue());
}
return encoded;
}

public static String decodeReferenceToken(final String s) {
String decoded = s;

List<String> reverseOrderedKeys = new ArrayList<String>(SUBSTITUTIONS.keySet());
Collections.reverse(reverseOrderedKeys);
for (String key : reverseOrderedKeys) {
decoded = decoded.replace(SUBSTITUTIONS.get(key), key);
}

return decoded;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.jsonschema2pojo.rules;

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

import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -30,7 +31,6 @@
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;


/**
* Applies the schema rules that represent a property definition.
*
Expand Down Expand Up @@ -70,9 +70,9 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef

String pathToProperty;
if (schema.getId() == null || schema.getId().getFragment() == null) {
pathToProperty = "#/properties/" + nodeName;
pathToProperty = "#/properties/" + JsonPointerUtils.encodeReferenceToken(nodeName);
} else {
pathToProperty = "#" + schema.getId().getFragment() + "/properties/" + nodeName;
pathToProperty = "#" + schema.getId().getFragment() + "/properties/" + JsonPointerUtils.encodeReferenceToken(nodeName);
}

Schema propertySchema = ruleFactory.getSchemaStore().create(schema, pathToProperty, ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public Schema getSuperSchema(JsonNode node, Schema schema, boolean followRefs) {
if (node.has("extends")) {
String path;
if (schema.getId().getFragment() == null) {
path = "#extends";
path = "#/extends";
} else {
path = "#" + schema.getId().getFragment() + "/extends";
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright © 2010-2020 Nokia
*
* 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
*
* 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.jsonschema2pojo;

import org.junit.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;


public class JsonPointerUtilsTest {

@Test
public void testEncodeReferenceToken() {
assertThat(JsonPointerUtils.encodeReferenceToken("com/vsv#..."), is("com~1vsv~2~3~3~3"));
assertThat(JsonPointerUtils.encodeReferenceToken("~1~2~01~3"), is("~01~02~001~03"));
}

@Test
public void testDecodeReferenceToken() {
assertThat(JsonPointerUtils.decodeReferenceToken("com~1vsv~2~3~3~3"), is("com/vsv#..."));
assertThat(JsonPointerUtils.decodeReferenceToken("~01~02~001~03"), is("~1~2~01~3"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
import com.fasterxml.jackson.databind.ObjectMapper;

public class PropertiesIT {
@Rule public Jsonschema2PojoRule schemaRule = new Jsonschema2PojoRule();
@Rule
public Jsonschema2PojoRule schemaRule = new Jsonschema2PojoRule();

private final ObjectMapper mapper = new ObjectMapper();

Expand Down Expand Up @@ -200,4 +201,15 @@ public void propertyNamesAreAllUpperCasesAndWithUnderScores() throws Exception {
assertThat(jsonified.has("PROPERTY_ONE_TWO_THREE"), is(true));
assertThat(jsonified.has("PROPERTY_ONE_TWO_THREE_four"), is(true));
}
}

@Test
public void propertyNamesWithSpecialCharacters() throws NoSuchMethodException, ClassNotFoundException {
ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/properties/propertiesWithSpecialCharacters.json", "com.example");
Class<?> generatedType = resultsClassLoader.loadClass("com.example.PropertiesWithSpecialCharacters");

assertNotNull(generatedType.getDeclaredMethod("getVersv"));
assertNotNull(generatedType.getDeclaredMethod("getFooBar"));
assertNotNull(generatedType.getDeclaredMethod("get$RfcNumber"));
assertNotNull(generatedType.getDeclaredMethod("getOrgHispDhisCommonFileTypeValueOptions"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"type" : "object",
"properties" : {
"VERSV#" : {
"type" : "string"
},
"foo#bar" : {
"type" : "string"
},
"${RFC_NUMBER}" : {
"type" : "string"
},
"org.hisp.dhis.common.FileTypeValueOptions" : {
"type" : "string"
}
}
}

0 comments on commit 9315b7b

Please sign in to comment.