Skip to content

Commit

Permalink
allow control over quoting and possibly unescaping schema and field s…
Browse files Browse the repository at this point in the history
…tring props. addresses #165 (#169)
  • Loading branch information
radai-rosenblatt authored Jul 15, 2021
1 parent 3b5f7be commit 7902120
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 4 deletions.
4 changes: 4 additions & 0 deletions helper/helper/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies {
implementation project(":helper:impls:helper-impl-18")
implementation project(":helper:impls:helper-impl-19")
implementation project(":helper:impls:helper-impl-110")
implementation "org.apache.commons:commons-text:1.9"
}

shadowJar {
Expand All @@ -37,6 +38,9 @@ shadowJar {
//do not pack slf4j
exclude(dependency("org.slf4j:slf4j-api:.*"))
}

//relocate commons-text to not conflict with anything that might be on a user's classpath
relocate 'org.apache.commons', 'com.linkedin.avroutil1.compatibility.shaded.org.apache.commons'
}

//"fat" sources jar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.avro.io.JsonDecoder;
import org.apache.avro.io.JsonEncoder;
import org.apache.avro.specific.SpecificRecord;
import org.apache.commons.text.StringEscapeUtils;


/**
Expand Down Expand Up @@ -649,10 +650,73 @@ public static Schema.Field createSchemaField(String name, Schema schema, String
* @return field property value as json literal, or null if no such property
*/
public static String getFieldPropAsJsonString(Schema.Field field, String propName) {
return ADAPTER.getFieldPropAsJsonString(field, propName);
return getFieldPropAsJsonString(field, propName, true, false);
}

/**
* returns the value of the specified field prop as a json literal.
* returns null if the field has no such property.
* optionally returns string literals as "naked" strings. also optionally unescapes any nested json
* inside such nested strings
* @param field the field who's property value we wish to get
* @param propName the name of the property
* @param quoteStringValues true to return string literals quoted. false to strip such quotes. false matches avro behaviour
* @param unescapeInnerJson true to unescape inner json inside string literals. true matches avro behaviour
* @return field property value as json literal, or null if no such property
*/
public static String getFieldPropAsJsonString(Schema.Field field, String propName, boolean quoteStringValues, boolean unescapeInnerJson) {
String value = ADAPTER.getFieldPropAsJsonString(field, propName);
return unquoteAndEscapeStringProp(value, quoteStringValues, unescapeInnerJson);
}

/**
* returns the value of the specified schema prop as a json literal.
* returns null if the schema has no such property.
* note that string values are returned quoted (as a proper json string literal)
* @param schema the schema who's property value we wish to get
* @param propName the name of the property
* @return schema property value as json literal, or null if no such property
*/
public static String getSchemaPropAsJsonString(Schema schema, String propName) {
return ADAPTER.getSchemaPropAsJsonString(schema, propName);
return getSchemaPropAsJsonString(schema, propName, true, false);
}

/**
* returns the value of the specified schema prop as a json literal.
* returns null if the schema has no such property.
* optionally returns string literals as "naked" strings. also optionally unescapes any nested json
* inside such nested strings
* @param schema the schema who's property value we wish to get
* @param propName the name of the property
* @param quoteStringValues true to return string literals quoted. false to strip such quotes. false matches avro behaviour
* @param unescapeInnerJson true to unescape inner json inside string literals. true matches avro behaviour
* @return schema property value as json literal, or null if no such property
*/
public static String getSchemaPropAsJsonString(Schema schema, String propName, boolean quoteStringValues, boolean unescapeInnerJson) {
String value = ADAPTER.getSchemaPropAsJsonString(schema, propName);
return unquoteAndEscapeStringProp(value, quoteStringValues, unescapeInnerJson);
}

private static String unquoteAndEscapeStringProp(String maybeAStringProp, boolean quoteStringValues, boolean unescapeInnerJson) {
if (maybeAStringProp == null) {
//no such prop
return null;
}
if (quoteStringValues && !unescapeInnerJson) {
//no changes actually required
return maybeAStringProp;
}
boolean isAStringValue = maybeAStringProp.startsWith("\"") && maybeAStringProp.endsWith("\"");
if (!isAStringValue) {
return maybeAStringProp;
}
String processed = maybeAStringProp;
if (!quoteStringValues) {
processed = processed.substring(1, processed.length() - 1); //remove enclosing quotes
}
if (unescapeInnerJson) {
processed = StringEscapeUtils.unescapeJson(processed);
}
return processed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,9 @@ public void testSchemaPropSupportOnClone() throws Exception {
Assert.assertEquals(AvroCompatibilityHelper.getSchemaPropAsJsonString(newSchema, "schemaNullProp"), "null");
Assert.assertEquals(AvroCompatibilityHelper.getSchemaPropAsJsonString(newSchema, "schemaBoolProp"), "false");
Assert.assertEquals(AvroCompatibilityHelper.getSchemaPropAsJsonString(newSchema, "schemaObjectProp"), "{\"e\":\"f\",\"g\":\"h\"}");

Assert.assertEquals(AvroCompatibilityHelper.getSchemaPropAsJsonString(newSchema, "schemaNestedJsonProp"), "\"{\\\"innerKey\\\" : \\\"innerValue\\\"}\"");
Assert.assertEquals(AvroCompatibilityHelper.getSchemaPropAsJsonString(newSchema, "schemaNestedJsonProp", false, false), "{\\\"innerKey\\\" : \\\"innerValue\\\"}");
Assert.assertEquals(AvroCompatibilityHelper.getSchemaPropAsJsonString(newSchema, "schemaNestedJsonProp", false, true), "{\"innerKey\" : \"innerValue\"}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"objectProp": {
"a": "b",
"c": "d"
}
},
"nestedJsonProp": "{\"innerKey\" : \"innerValue\"}"
},
{
"name": "intField",
Expand All @@ -30,5 +31,6 @@
"schemaObjectProp": {
"e": "f",
"g": "h"
}
},
"schemaNestedJsonProp": "{\"innerKey\" : \"innerValue\"}"
}

0 comments on commit 7902120

Please sign in to comment.