From 7fe7047e6e6aec82f78cca366f0604dd606a5e7e Mon Sep 17 00:00:00 2001 From: Taras Tsugrii Date: Wed, 25 Jul 2018 13:58:02 -0700 Subject: [Skylark] Support dictionaries in structs when serializing them using struct.to_json. Dictionaries are frequently used for generic configuration descriptions especially given that `struct` is not allowed in build files. In order to be JSON-compatible, only dictionaries with string keys are allowed. Technically Python also allows integers and booleans by automatically converting them to strings, but this is confusing and not necessarily a good thing to do. Fixes #5542 Closes #5543. PiperOrigin-RevId: 206049754 --- .../google/devtools/build/lib/packages/Info.java | 34 ++++++++++++++++++---- .../build/lib/skylarkbuildapi/StructApi.java | 5 ++-- 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'src/main') diff --git a/src/main/java/com/google/devtools/build/lib/packages/Info.java b/src/main/java/com/google/devtools/build/lib/packages/Info.java index 775b8e0a80..4bc863bafd 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/Info.java +++ b/src/main/java/com/google/devtools/build/lib/packages/Info.java @@ -30,6 +30,7 @@ import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.syntax.Printer; import com.google.devtools.build.lib.syntax.Runtime; +import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkType; import com.google.protobuf.TextFormat; @@ -218,8 +219,8 @@ public abstract class Info implements ClassObject, StructApi, Serializable { return sb.toString(); } - private void printProtoTextMessage( - ClassObject object, StringBuilder sb, int indent, Location loc) throws EvalException { + private void printProtoTextMessage(ClassObject object, StringBuilder sb, int indent, Location loc) + throws EvalException { // For determinism sort the fields alphabetically. List fields = new ArrayList<>(object.getFieldNames()); Collections.sort(fields); @@ -260,8 +261,7 @@ public abstract class Info implements ClassObject, StructApi, Serializable { } private void printProtoTextMessage( - String key, Object value, StringBuilder sb, int indent, Location loc) - throws EvalException { + String key, Object value, StringBuilder sb, int indent, Location loc) throws EvalException { if (value instanceof SkylarkList) { for (Object item : ((SkylarkList) value)) { // TODO(bazel-team): There should be some constraint on the fields of the structs @@ -297,8 +297,7 @@ public abstract class Info implements ClassObject, StructApi, Serializable { return sb.toString(); } - private void printJson( - Object value, StringBuilder sb, Location loc, String container, String key) + private void printJson(Object value, StringBuilder sb, Location loc, String container, String key) throws EvalException { if (value == Runtime.NONE) { sb.append("null"); @@ -315,6 +314,29 @@ public abstract class Info implements ClassObject, StructApi, Serializable { printJson(((ClassObject) value).getValue(field), sb, loc, "struct field", field); } sb.append("}"); + } else if (value instanceof SkylarkDict) { + sb.append("{"); + String join = ""; + for (Map.Entry entry : ((SkylarkDict) value).entrySet()) { + sb.append(join); + join = ","; + if (!(entry.getKey() instanceof String)) { + String errorMessage = + "Keys must be a string but got a " + + EvalUtils.getDataTypeName(entry.getKey()) + + " for " + + container; + if (key != null) { + errorMessage += " '" + key + "'"; + } + throw new EvalException(loc, errorMessage); + } + sb.append("\""); + sb.append(entry.getKey()); + sb.append("\":"); + printJson(entry.getValue(), sb, loc, "dict value", String.valueOf(entry.getKey())); + } + sb.append("}"); } else if (value instanceof List) { sb.append("["); String join = ""; diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java index 5c0974975c..49d90e5ac7 100644 --- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java +++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/StructApi.java @@ -60,8 +60,9 @@ public interface StructApi extends SkylarkValue { name = "to_json", doc = "Creates a JSON string from the struct parameter. This method only works if all " - + "struct elements (recursively) are strings, ints, booleans, other structs or a " - + "list of these types. Quotes and new lines in strings are escaped. " + + "struct elements (recursively) are strings, ints, booleans, other structs, a " + + "list of these types or a dictionary with string keys and values of these types. " + + "Quotes and new lines in strings are escaped. " + "Examples:
"
               + "struct(key=123).to_json()\n# {\"key\":123}\n\n"
               + "struct(key=True).to_json()\n# {\"key\":true}\n\n"
-- 
cgit v1.2.3