2013-06-05 171 views
11

我有一個iOS應用程序需要處理來自Web服務的響應。響應是一個包含序列化JSON對象,看起來像這樣一個序列化JSON字符串:如何使用NSJSONSerialization反序列化轉義的JSON字符串?

"{ \"name\" : \"Bob\", \"age\" : 21 }" 

注意,該響應是一個JSON字符串,而不是一個JSON對象。我需要做的是反序列化的字符串,讓我得到這個:

{ "name" : "Bob", "age" : 21 } 

,然後我可以用+[NSJSONSerialization JSONObjectWithData:options:error:]反序列化到NSDictionary

但是,我該如何做第一步呢?也就是說,我如何「unes​​cape」字符串,以便我有一個序列化的JSON對象? +[NSJSONSerialization JSONObjectWithData:options:error:]只適用於頂級對象是數組或字典;它不適用於字符串。

我最後寫my own JSON string parser,我希望符合section 2.5 of RFC 4627。但我懷疑我忽略了使用NSJSONSerialization或其他一些可用方法來做到這一點的簡單方法。

+0

就切斷前端和後端quoteß然後替換所有'\「單曲用'''。 – 2013-06-05 19:49:59

+0

然後你重複每個可能的轉義序列的迴避過程(有很多)。 – 2013-06-05 19:52:16

+0

'\ uXXXX'轉義序列使得簡單的搜索和替換變得困難。 –

回答

24

如果您有嵌套JSON,那麼就請撥打JSONObjectWithData兩次:

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; } 
+0

當我嘗試了,我收到一條錯誤消息。它似乎只在頂級對象是數組或字典時才起作用。 –

+0

@KristopherJohnson:我試過這段代碼,它對我很有用。你需要第一步中的NSJSONReadingAllowFragments選項。 –

+0

我發誓我之前試過'NSJSONReadingAllowFragments',它沒有用,但現在它確實。謝謝你讓我再試一次。 –

0

將字符串數據:

NSString *string = @"{ \"name\" : \"Bob\", \"age\" : 21 }"; 
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; 
NSError *error; 
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 
+0

我有一個'NSData'對象,當我調用' NSJSONSerialization'。所以就好像你的代碼是'string = @「\」{\\「name \\」:\\「Bob \\」,\\「age \\」:21} \「」;' –

+0

你得到那個字符串?這聽起來像是在管道中的某處,它被雙重轉義,這就是你需要調試的地方。 –

+1

Web服務將其雙重轉義。你和我可能會同意,它不應該,但它確實,我必須處理它。 –

0

就切斷開頭和結尾的引號,然後替換所有的\「s的」:

NSString *sub = [original substringWithRange:(NSRange){ 1, original.length - 2 }]; 
NSString *unescaped = [sub stringByReplacingOccurrencesOfString:@"\\\" withString:@"\"]; 
+0

這是我最初的解決方法,但只有在字符串中沒有其他特殊字符時才起作用('\ n','\ t','\ u1234'等)。但我不能這樣認爲。 –

+0

JSON字符串需要轉義這些字符。所以,當一個編碼器被應用時,它只會逃避逃逸:「(\\ n)」 – CouchDeveloper

0

應該先問一下,爲什麼服務器不包括作爲子結構的JSON。

但無論如何。你得到的字符串似乎是逃過 JSON。 究竟是什麼表示,完全取決於網絡服務開發商。我懷疑,那只是雙引號和逃跑本身已經逃脫了\。生成的字符串不是「序列化的」 - JSON已經序列化 - 但編碼爲。爲了將其恢復 - 你需要「UNESCAPE」或再次解碼它:

小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}

+1

你的例子在輸入'{\「name \」:\「Bob \」,\「age \」:21}'上運行。但實際的服務器響應是(據我瞭解)'「{\」name \「:\」Bob \「,\」age \「:21}」'(注意前導和尾部的引號)。這就是NSJSONReadingAllowFragments工作的原因。 –

+0

好的,這意味着,NSJSONSerialization將其解釋爲JSON字符串,然後返回此頂級對象 - 這是NSString。因此,它有效地將「JSON字符串解碼器」應用於給定的字符串。這可能會起作用 - 但我會反正澄清給定的字符串是如何被Web服務解碼的。 – CouchDeveloper