From 320e944bb2b54bf6946d5226b4fbb9ae907725af Mon Sep 17 00:00:00 2001 From: Zihua Li Date: Thu, 22 Dec 2022 14:15:18 +0800 Subject: [PATCH] Support unicode sequence escaping --- Package.swift | 2 +- .../SwiftJSONFormatter/ArrayIterator.swift | 6 +-- .../SwiftJSONFormatter.swift | 38 +++++++++++++------ .../SwiftJSONFormatterTests.swift | 33 ++++++++++++++++ 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/Package.swift b/Package.swift index efbcc3d..a947a0d 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "SwiftJSONFormatter", platforms: [ - .macOS(.v10_13), + .macOS(.v10_15), .iOS(.v12), .tvOS(.v12), .watchOS(.v5) diff --git a/Sources/SwiftJSONFormatter/ArrayIterator.swift b/Sources/SwiftJSONFormatter/ArrayIterator.swift index 5f379af..d00c3b2 100644 --- a/Sources/SwiftJSONFormatter/ArrayIterator.swift +++ b/Sources/SwiftJSONFormatter/ArrayIterator.swift @@ -19,9 +19,9 @@ class ArrayIterator { self.array = array } - func peekNext() -> T? { - if head + 1 < array.count { - return array[head + 1] + func peekNext(n: Int = 1) -> T? { + if head + n < array.count { + return array[head + n] } return nil } diff --git a/Sources/SwiftJSONFormatter/SwiftJSONFormatter.swift b/Sources/SwiftJSONFormatter/SwiftJSONFormatter.swift index caee960..ce12846 100644 --- a/Sources/SwiftJSONFormatter/SwiftJSONFormatter.swift +++ b/Sources/SwiftJSONFormatter/SwiftJSONFormatter.swift @@ -1,7 +1,7 @@ +import Foundation + public struct SwiftJSONFormatter { - public private(set) var text = "Hello, World!" - - private static func format(_ value: String, indent: String, newLine: String, separator: String) -> String { + private static func format(_ value: String, indent: String, newLine: String, separator: String, unescapeUnicodeSequence: Bool) -> String { var formatted = "" let chars = ArrayIterator(Array(value)) @@ -27,7 +27,7 @@ public struct SwiftJSONFormatter { formatted.append(newLine) formatted += "\(String(repeating: indent, count: indentLevel))\(char)" case "\"": - let string = consumeString(chars) + let string = consumeString(chars, unescapeUnicodeSequence: unescapeUnicodeSequence) formatted.append(string) case ",": consumeWhitespaces(chars) @@ -52,12 +52,12 @@ public struct SwiftJSONFormatter { return formatted } - public static func beautify(_ value: String, indent: String = " ") -> String { - format(value, indent: indent, newLine: "\n", separator: " ") + public static func beautify(_ value: String, indent: String = " ", unescapeUnicodeSequence: Bool = false) -> String { + format(value, indent: indent, newLine: "\n", separator: " ", unescapeUnicodeSequence: unescapeUnicodeSequence) } - public static func minify(_ value: String) -> String { - format(value, indent: "", newLine: "", separator: "") + public static func minify(_ value: String, unescapeUnicodeSequence: Bool = false) -> String { + format(value, indent: "", newLine: "", separator: "", unescapeUnicodeSequence: unescapeUnicodeSequence) } private static func consumeWhitespaces(_ iter: ArrayIterator) { @@ -70,7 +70,22 @@ public struct SwiftJSONFormatter { } } - private static func consumeString(_ iter: ArrayIterator) -> String { + private static func performUnescaping(_ jsonString: String, unescapeUnicodeSequence: Bool) -> String { + if unescapeUnicodeSequence { + let decoder = JSONDecoder() + if let data = jsonString.data(using: .utf8), let result = try? decoder.decode(String.self, from: data) { + let encoder = JSONEncoder() + encoder.outputFormatting = .withoutEscapingSlashes + if let encoded = try? encoder.encode(result), let encodedString = String(data: encoded, encoding: .utf8) { + return encodedString + } + } + } + + return jsonString + } + + private static func consumeString(_ iter: ArrayIterator, unescapeUnicodeSequence: Bool) -> String { var string = "\"" var escaping = false while true { @@ -88,13 +103,14 @@ public struct SwiftJSONFormatter { escaping = true } if char == "\"" { - return string + return performUnescaping(string, unescapeUnicodeSequence: unescapeUnicodeSequence) } } } else { break } } - return string + + return performUnescaping(string, unescapeUnicodeSequence: unescapeUnicodeSequence) } } diff --git a/Tests/SwiftJSONFormatterTests/SwiftJSONFormatterTests.swift b/Tests/SwiftJSONFormatterTests/SwiftJSONFormatterTests.swift index 441ea54..6839e3f 100644 --- a/Tests/SwiftJSONFormatterTests/SwiftJSONFormatterTests.swift +++ b/Tests/SwiftJSONFormatterTests/SwiftJSONFormatterTests.swift @@ -95,4 +95,37 @@ final class SwiftJSONFormatterTests: XCTestCase { "contains\"", "\\\"quotes\n"] """#), #"["string","contains\"","\\\"quotes\n"]"#) } + + func testUnicodeEscaping() { + XCTAssertEqual(SwiftJSONFormatter.minify( + #"["15\u00f8C"]"#, + unescapeUnicodeSequence: false + ), + #"["15\u00f8C"]"# + ) + XCTAssertEqual(SwiftJSONFormatter.minify( + #"["15\u00f8C"]"#, + unescapeUnicodeSequence: true + ), + #"["15øC"]"# + ) + XCTAssertEqual(SwiftJSONFormatter.minify( + #"["Bien pr\u00e9parer votre s\u00e9jour"]"#, + unescapeUnicodeSequence: true + ), + #"["Bien préparer votre séjour"]"# + ) + XCTAssertEqual(SwiftJSONFormatter.minify( + #"["\u4F60\u597D\n \uD83D\uDE04\uD83D\uDE04\uD834\uDF06"]"#, + unescapeUnicodeSequence: true + ), + #"["你好\n 😄😄𝌆"]"# + ) + XCTAssertEqual(SwiftJSONFormatter.minify( + #"["https://example.com"]"#, + unescapeUnicodeSequence: true + ), + #"["https://example.com"]"# + ) + } }