Skip to content

Commit 320e944

Browse files
committed
Support unicode sequence escaping
1 parent 843456f commit 320e944

4 files changed

Lines changed: 64 additions & 15 deletions

File tree

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import PackageDescription
44
let package = Package(
55
name: "SwiftJSONFormatter",
66
platforms: [
7-
.macOS(.v10_13),
7+
.macOS(.v10_15),
88
.iOS(.v12),
99
.tvOS(.v12),
1010
.watchOS(.v5)

Sources/SwiftJSONFormatter/ArrayIterator.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ class ArrayIterator<T> {
1919
self.array = array
2020
}
2121

22-
func peekNext() -> T? {
23-
if head + 1 < array.count {
24-
return array[head + 1]
22+
func peekNext(n: Int = 1) -> T? {
23+
if head + n < array.count {
24+
return array[head + n]
2525
}
2626
return nil
2727
}

Sources/SwiftJSONFormatter/SwiftJSONFormatter.swift

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import Foundation
2+
13
public struct SwiftJSONFormatter {
2-
public private(set) var text = "Hello, World!"
3-
4-
private static func format(_ value: String, indent: String, newLine: String, separator: String) -> String {
4+
private static func format(_ value: String, indent: String, newLine: String, separator: String, unescapeUnicodeSequence: Bool) -> String {
55
var formatted = ""
66

77
let chars = ArrayIterator(Array(value))
@@ -27,7 +27,7 @@ public struct SwiftJSONFormatter {
2727
formatted.append(newLine)
2828
formatted += "\(String(repeating: indent, count: indentLevel))\(char)"
2929
case "\"":
30-
let string = consumeString(chars)
30+
let string = consumeString(chars, unescapeUnicodeSequence: unescapeUnicodeSequence)
3131
formatted.append(string)
3232
case ",":
3333
consumeWhitespaces(chars)
@@ -52,12 +52,12 @@ public struct SwiftJSONFormatter {
5252
return formatted
5353
}
5454

55-
public static func beautify(_ value: String, indent: String = " ") -> String {
56-
format(value, indent: indent, newLine: "\n", separator: " ")
55+
public static func beautify(_ value: String, indent: String = " ", unescapeUnicodeSequence: Bool = false) -> String {
56+
format(value, indent: indent, newLine: "\n", separator: " ", unescapeUnicodeSequence: unescapeUnicodeSequence)
5757
}
5858

59-
public static func minify(_ value: String) -> String {
60-
format(value, indent: "", newLine: "", separator: "")
59+
public static func minify(_ value: String, unescapeUnicodeSequence: Bool = false) -> String {
60+
format(value, indent: "", newLine: "", separator: "", unescapeUnicodeSequence: unescapeUnicodeSequence)
6161
}
6262

6363
private static func consumeWhitespaces(_ iter: ArrayIterator<String.Element>) {
@@ -70,7 +70,22 @@ public struct SwiftJSONFormatter {
7070
}
7171
}
7272

73-
private static func consumeString(_ iter: ArrayIterator<String.Element>) -> String {
73+
private static func performUnescaping(_ jsonString: String, unescapeUnicodeSequence: Bool) -> String {
74+
if unescapeUnicodeSequence {
75+
let decoder = JSONDecoder()
76+
if let data = jsonString.data(using: .utf8), let result = try? decoder.decode(String.self, from: data) {
77+
let encoder = JSONEncoder()
78+
encoder.outputFormatting = .withoutEscapingSlashes
79+
if let encoded = try? encoder.encode(result), let encodedString = String(data: encoded, encoding: .utf8) {
80+
return encodedString
81+
}
82+
}
83+
}
84+
85+
return jsonString
86+
}
87+
88+
private static func consumeString(_ iter: ArrayIterator<String.Element>, unescapeUnicodeSequence: Bool) -> String {
7489
var string = "\""
7590
var escaping = false
7691
while true {
@@ -88,13 +103,14 @@ public struct SwiftJSONFormatter {
88103
escaping = true
89104
}
90105
if char == "\"" {
91-
return string
106+
return performUnescaping(string, unescapeUnicodeSequence: unescapeUnicodeSequence)
92107
}
93108
}
94109
} else {
95110
break
96111
}
97112
}
98-
return string
113+
114+
return performUnescaping(string, unescapeUnicodeSequence: unescapeUnicodeSequence)
99115
}
100116
}

Tests/SwiftJSONFormatterTests/SwiftJSONFormatterTests.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,37 @@ final class SwiftJSONFormatterTests: XCTestCase {
9595
"contains\"", "\\\"quotes\n"]
9696
"""#), #"["string","contains\"","\\\"quotes\n"]"#)
9797
}
98+
99+
func testUnicodeEscaping() {
100+
XCTAssertEqual(SwiftJSONFormatter.minify(
101+
#"["15\u00f8C"]"#,
102+
unescapeUnicodeSequence: false
103+
),
104+
#"["15\u00f8C"]"#
105+
)
106+
XCTAssertEqual(SwiftJSONFormatter.minify(
107+
#"["15\u00f8C"]"#,
108+
unescapeUnicodeSequence: true
109+
),
110+
#"["15øC"]"#
111+
)
112+
XCTAssertEqual(SwiftJSONFormatter.minify(
113+
#"["Bien pr\u00e9parer votre s\u00e9jour"]"#,
114+
unescapeUnicodeSequence: true
115+
),
116+
#"["Bien préparer votre séjour"]"#
117+
)
118+
XCTAssertEqual(SwiftJSONFormatter.minify(
119+
#"["\u4F60\u597D\n \uD83D\uDE04\uD83D\uDE04\uD834\uDF06"]"#,
120+
unescapeUnicodeSequence: true
121+
),
122+
#"["你好\n 😄😄𝌆"]"#
123+
)
124+
XCTAssertEqual(SwiftJSONFormatter.minify(
125+
#"["https://example.com"]"#,
126+
unescapeUnicodeSequence: true
127+
),
128+
#"["https://example.com"]"#
129+
)
130+
}
98131
}

0 commit comments

Comments
 (0)