Skip to content

Commit 5f3977d

Browse files
authored
Centralize and improve String literal handling (#125)
* Share code for escape String literals * Add support for escaping strings w/ new line characters
1 parent 228366b commit 5f3977d

File tree

5 files changed

+51
-35
lines changed

5 files changed

+51
-35
lines changed

json_serializable/lib/src/generator_helper.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,7 @@ void $toJsonMapHelperName(String key, dynamic value) {
322322

323323
String _safeNameAccess(FieldElement field) {
324324
var name = jsonKeyFor(field).name ?? field.name;
325-
// TODO(kevmoo): JsonKey.name could also have quotes and other silly.
326-
return name.contains(r'$') ? "r'$name'" : "'$name'";
325+
return escapeDartString(name);
327326
}
328327

329328
JsonSerializable _valueForAnnotation(ConstantReader annotation) =>

json_serializable/lib/src/json_literal_generator.dart

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import 'package:source_gen/source_gen.dart';
1212

1313
import 'package:json_annotation/json_annotation.dart';
1414

15+
import 'utils.dart';
16+
1517
class JsonLiteralGenerator extends GeneratorForAnnotation<JsonLiteral> {
1618
const JsonLiteralGenerator();
1719

@@ -44,7 +46,7 @@ class JsonLiteralGenerator extends GeneratorForAnnotation<JsonLiteral> {
4446
String _jsonLiteralAsDart(dynamic value, bool asConst) {
4547
if (value == null) return 'null';
4648

47-
if (value is String) return _jsonStringAsDart(value);
49+
if (value is String) return escapeDartString(value);
4850

4951
if (value is bool || value is num) return value.toString();
5052

@@ -74,7 +76,7 @@ String _jsonMapAsDart(Map<String, dynamic> value, bool asConst) {
7476
} else {
7577
buffer.writeln(',');
7678
}
77-
buffer.write(_jsonStringAsDart(k));
79+
buffer.write(escapeDartString(k));
7880
buffer.write(':');
7981
buffer.write(_jsonLiteralAsDart(v, asConst));
8082
});
@@ -83,34 +85,3 @@ String _jsonMapAsDart(Map<String, dynamic> value, bool asConst) {
8385

8486
return buffer.toString();
8587
}
86-
87-
String _jsonStringAsDart(String value) {
88-
var containsSingleQuote = value.contains("'");
89-
var contains$ = value.contains(r'$');
90-
91-
if (containsSingleQuote) {
92-
if (value.contains('"')) {
93-
// `value` contains both single and double quotes as well as `$`.
94-
// The only safe way to wrap the content is to escape all of the
95-
// problematic characters.
96-
var string = value
97-
.replaceAll('\$', '\\\$')
98-
.replaceAll('"', '\\"')
99-
.replaceAll("'", "\\'");
100-
return "'$string'";
101-
} else if (contains$) {
102-
// `value` contains "'" and "$", but not '"'.
103-
// Safely wrap it in a raw string within double-quotes.
104-
return 'r"$value"';
105-
}
106-
return '"$value"';
107-
} else if (contains$) {
108-
// `value` contains "$", but no "'"
109-
// wrap it in a raw string using single quotes
110-
return "r'$value'";
111-
}
112-
113-
// `value` contains no problematic characters - except for '"' maybe.
114-
// Wrap it in standard single-quotes.
115-
return "'$value'";
116-
}

json_serializable/lib/src/utils.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,43 @@ import 'package:analyzer/src/dart/resolver/inheritance_manager.dart'
1111

1212
import 'package:source_gen/source_gen.dart';
1313

14+
/// Returns a quoted String literal for [value] that can be used in generated
15+
/// Dart code.
16+
// TODO: still need handle triple singe/double quotes within `value`
17+
String escapeDartString(String value) {
18+
if (value.contains('\n')) {
19+
return "r'''\n$value'''";
20+
}
21+
22+
var containsDollar = value.contains(r'$');
23+
24+
if (value.contains("'")) {
25+
if (value.contains('"')) {
26+
// `value` contains both single and double quotes.
27+
// The only safe way to wrap the content is to escape all of the
28+
// problematic characters.
29+
var string = value
30+
.replaceAll(r'$', r'\$')
31+
.replaceAll('"', r'\"')
32+
.replaceAll("'", r"\'");
33+
return "'$string'";
34+
} else if (containsDollar) {
35+
// `value` contains "'" and "$", but not '"'.
36+
// Safely wrap it in a raw string within double-quotes.
37+
return 'r"$value"';
38+
}
39+
return '"$value"';
40+
} else if (containsDollar) {
41+
// `value` contains "$", but no "'"
42+
// wrap it in a raw string using single quotes
43+
return "r'$value'";
44+
}
45+
46+
// `value` contains no problematic characters - except for '"' maybe.
47+
// Wrap it in standard single-quotes.
48+
return "'$value'";
49+
}
50+
1451
String commonNullPrefix(
1552
bool nullable, String expression, String unsafeExpression) =>
1653
nullable

json_serializable/test/test_files/data.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[
2+
"\nnew lines are fun!\n",
23
"simple string",
34
"'string with single quotes'",
45
"\"string with double quotes\"",

json_serializable/test/test_files/json_literal.g.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ part of 'json_literal.dart';
1111
// **************************************************************************
1212

1313
final _$dataJsonLiteral = [
14+
r'''
15+
16+
new lines are fun!
17+
''',
1418
'simple string',
1519
"'string with single quotes'",
1620
'"string with double quotes"',
@@ -38,6 +42,10 @@ final _$dataJsonLiteral = [
3842
}
3943
];
4044
const _$asConstJsonLiteral = const [
45+
r'''
46+
47+
new lines are fun!
48+
''',
4149
'simple string',
4250
"'string with single quotes'",
4351
'"string with double quotes"',

0 commit comments

Comments
 (0)