読む前に
以下の内容ですが、私のJSONに対する現状の知識不足が原因でした。
JSONのエスケープ対象として/
が含まれており、JSONとして出力した際に\/
となるのは正しい挙動となります。
2重エスケープされていたのは" {"data" : "aa\\/bbb"}"
と ""
で囲われていたのが原因でした。
私と同様に上記を把握しておらず、解決方法を探した方が見つけられるように本記事は削除せずそのまま残すことにします。
##はじめに
EncodableのStructをencodeしdataに変換、 String(data: Data, encoding: String.Encoding)を使って文字列にするといった処理を行っていました。
その際にEscape周りで意外な挙動があったので記載します。
##検証環境
以下の環境を使用しています。
- macOS High Sierra Version 10.14.2
- Xcode Version 10.1.0
- Swift Version 4.2
##事象
struct Debug: Codable {
let date = "[aaa/bbb]" // / が含まれるように文字列を定義
}
let encoder = JSONEncoder()
let data = try! encoder.encode(Debug())
let string = String(data: data, encoding: .utf8) // DataからJSON文字列生成
print(string)
上記のコードを実行した際に、{"date":"[aaa/bbb]"}
の文字列が取得できることを期待しました。
しかし実際に取得できた文字列は{"date":"[aaa\/bbb]"}
になります。
タイトルの通り /
が2重にエスケープされてしまい \\/
となった結果、文字列化した際に\/
となり\
が余分につくという事象が発生しました。
stringをForced Unwrapしなかった場合は Optional("{\"date\":\"[aaa\\/bbb]\"}")
となり、2重にエスケープされていることがわかります。
対応
以下のことを試しました。
-
/
を\u{002F}
に変換する。(\u{002F}
は/
のUnicodeコード表現です) - CodableのJSONEncoderではなくJSONSerialization経由で文字列化する
- String(data: Data, eocoding: String.Encoding)のencodingパラメータを変える
ただ、上記の方法ではどれも解決に至りませんでした。
最終的にとった方法は以下になります。\\/
を/
に置き換えました。
これで{"date":"[aaa/bbb]"}
という期待したとおりの文字列を取得することができました。
let string = String(data: data, encoding: .utf8)?.replacingOccurrences(of: "\\/", with: "/")
##最後に
正直にいって本記事の対応はあまり良いと思えません。このあたり知見がある方や、このようにしたほうがいいのではないか等ありましたらコメントいただけますと幸いです。