Webサービスからの応答を処理する必要があるiOSアプリがあります。応答は、シリアル化されたJSONオブジェクトを含むシリアル化されたJSON文字列であり、次のようになります。
"{ \"name\" : \"Bob\", \"age\" : 21 }"
この応答はJSONstringであり、JSONオブジェクトではないことに注意してください。私がする必要があるのは、文字列を逆シリアル化することです。これにより、次のようになります。
{ "name" : "Bob", "age" : 21 }
そして、+[NSJSONSerialization JSONObjectWithData:options:error:]
を使用してそれをNSDictionary
に逆シリアル化できます。
しかし、どうすればその最初のステップを実行できますか?つまり、文字列を「エスケープ解除」して、シリアル化されたJSONオブジェクトを作成するにはどうすればよいですか? +[NSJSONSerialization JSONObjectWithData:options:error:]
は、最上位オブジェクトが配列または辞書である場合にのみ機能します。文字列では機能しません。
最終的に 自分のJSON文字列パーサー と書きました。これは RFC 4627のセクション2.5 に準拠していると思います。しかし、NSJSONSerialization
または他の利用可能な方法を使用してこれを行う簡単な方法を見落としているのではないかと思います。
JSONをネストしている場合は、JSONObjectWithData
を2回呼び出すだけです。
NSString *string = @"\"{ \\\"name\\\" : \\\"Bob\\\", \\\"age\\\" : 21 }\"";
// --> the string
// "{ \"name\" : \"Bob\", \"age\" : 21 }"
NSError *error;
NSString *outerJson = [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments error:&error];
// --> the string
// { "name" : "Bob", "age" : 21 }
NSDictionary *innerJson = [NSJSONSerialization JSONObjectWithData:[outerJson dataUsingEncoding:NSUTF8StringEncoding]
options:0 error:&error];
// --> the dictionary
// { age = 21; name = Bob; }
文字列をデータに変換します。
NSString *string = @"{ \"name\" : \"Bob\", \"age\" : 21 }";
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
先頭と末尾の引用符を切り取り、すべての「」を「:」に置き換えます。
NSString *sub = [original substringWithRange:(NSRange){ 1, original.length - 2 }];
NSString *unescaped = [sub stringByReplacingOccurrencesOfString:@"\\\" withString:@"\"];
最初に、サーバーがサブ構造としてJSONを含まない理由を尋ねる必要があります。
とにかく。取得した文字列はescapedJSONのようです。それが実際に意味することは、完全にWebサービス開発者次第です。二重引用符とエスケープ自体だけがエスケープでエスケープされているのではないかと思います\
。結果の文字列は「シリアル化」されません-JSONはすでにシリアル化されています-しかしエンコードされています。元に戻すには、「エスケープ解除」するか、デコードする必要があります。
小さなC++スニペットがその方法を示しています(Objective-Cを要求したことは知っていますが、これは簡単すぎます)。
編集:コードはUTF-16およびUTF-32(任意のエンディアン)でも機能するはずです。エンコーダーが私が疑うことを機械的に実行した場合は、エスケープされたUnicode文字でも機能するはずです。\u1234など.
編集-いいえ、UTF-16およびUTF-32では機能しません。そのためにサンプルを修正する必要があります(これは簡単です)。ただし、UTF-8を使用していることを確認してください。ほとんどの場合これが当てはまります。
#include <iostream>
char input[] = u8R"___({ \"name\" : \"Bob\", \"age\" : 21 })___";
// Unescapes the character sequence "in-situ".
// Returns a pointer to "past-the-end" of the unescaped string.
static char* unescape(char* first, char* last) {
char* dest = first;
while (first != last) {
if (*first == '\\') {
++first;
}
*dest++ = *first++;
}
return dest;
}
int main(int argc, const char * argv[])
{
char* first = input;
char* last = first + strlen(input);
std::string s(input, unescape(first, last));
std::cout << s << std::endl;
return 0;
}
プリント:
{"名前": "ボブ"、 "年齢":21}