2012-10-19 26 views
1

我有一個iPhone/iPad應用程序有一些數據存儲在屬性列表中。我正在擴展應用程序以處理多次出現的相同數據,不幸的是,它與正確列表綁定太多,只是將整批轉換爲sqlite3或核心數據,所以我需要將屬性列表存儲在blob字段中的sqlite3表中。iPhone:sqlite3 blob字段不正確unserialising

我已經使用[NSPropertySerialisation dataFromPropertyList:format:errorDescription]對數據進行了序列化,並且可以在測試中成功地將其解串爲NSDictionary。我可以很好地存儲數據,但是當我檢索它時,我無法正確回到[NSPropertySerialisation propertyListFromData]中。

這裏是的NSDictionary對象:

{ 
    companydetails =  { 
     address1 = "Company Address"; 
     "company_name" = "New Survey Company"; 
    }; 
} 

轉換爲串行化的數據用:

NSData *data = [NSPropertyListSerialization dataFromPropertyList:[Survey data] format:NSPropertyListXMLFormat_v1_0 errorDescription:&errStr]; 

其中產量:

<3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f 64696e67 3d225554 462d3822 3f3e0a3c 21444f43 54595045 20706c69 73742050 55424c49 4320222d 2f2f4170 706c652f 2f445444 20504c49 53542031 2e302f2f 454e2220 22687474 703a2f2f 7777772e 6170706c 652e636f 6d2f4454 44732f50 726f7065 7274794c 6973742d 312e302e 64746422 3e0a3c70 6c697374 20766572 73696f6e 3d22312e 30223e0a 3c646963 743e0a09 3c6b6579 3e636f6d 70616e79 64657461 696c733c 2f6b6579 3e0a093c 64696374 3e0a0909 3c6b6579 3e616464 72657373 313c2f6b 65793e0a 09093c73 7472696e 673e436f 6d70616e 79204164 64726573 733c2f73 7472696e 673e0a09 093c6b65 793e636f 6d70616e 795f6e61 6d653c2f 6b65793e 0a09093c 73747269 6e673e4e 65772053 75727665 7920436f 6d70616e 793c2f73 7472696e 673e0a09 3c2f6469 63743e0a 3c2f6469 63743e0a 3c2f706c 6973743e 0a> 

我可以再unserialise此用:

NSDictionary *revData = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:&errStr]; 

它返回我回:

{ 
    companydetails =  { 
     address1 = "Company Address"; 
     "company_name" = "New Survey Company"; 
    }; 
} 

這是我的表架構:

CREATE TABLE surveys ( id integer primary key autoincrement, company_name text, address text, surveydata blob); 

這裏是我的數據庫插入語句:

NSString *insSQL = [NSString stringWithFormat:@"INSERT INTO surveys (company_name, address, surveydata) VALUES (\"%@\", \"%@\", \"%@\")", SURVEY_DEFAULT_COMPANY, SURVEY_DEFAULT_ADDRESS, data]; 
const char *sql = [insSQL UTF8String]; 

這將產生:

INSERT INTO surveys (company_name, address, surveydata) VALUES ("New Survey Company", "Company Address", "<3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f 64696e67 3d225554 462d3822 3f3e0a3c 21444f43 54595045 20706c69 73742050 55424c49 4320222d 2f2f4170 706c652f 2f445444 20504c49 53542031 2e302f2f 454e2220 22687474 703a2f2f 7777772e 6170706c 652e636f 6d2f4454 44732f50 726f7065 7274794c 6973742d 312e302e 64746422 3e0a3c70 6c697374 20766572 73696f6e 3d22312e 30223e0a 3c646963 743e0a09 3c6b6579 3e636f6d 70616e79 64657461 696c733c 2f6b6579 3e0a093c 64696374 3e0a0909 3c6b6579 3e616464 72657373 313c2f6b 65793e0a 09093c73 7472696e 673e436f 6d70616e 79204164 64726573 733c2f73 7472696e 673e0a09 093c6b65 793e636f 6d70616e 795f6e61 6d653c2f 6b65793e 0a09093c 73747269 6e673e4e 65772053 75727665 7920436f 6d70616e 793c2f73 7472696e 673e0a09 3c2f6469 63743e0a 3c2f6469 63743e0a 3c2f706c 6973743e 0a>") 

當我檢索另一個函數從表中的數據,我得到正確的BLOB數據傳回:

<3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f 64696e67 3d225554 462d3822 3f3e0a3c 21444f43 54595045 20706c69 73742050 55424c49 4320222d 2f2f4170 706c652f 2f445444 20504c49 53542031 2e302f2f 454e2220 22687474 703a2f2f 7777772e 6170706c 652e636f 6d2f4454 44732f50 726f7065 7274794c 6973742d 312e302e 64746422 3e0a3c70 6c697374 20766572 73696f6e 3d22312e 30223e0a 3c646963 743e0a09 3c6b6579 3e636f6d 70616e79 64657461 696c733c 2f6b6579 3e0a093c 64696374 3e0a0909 3c6b6579 3e616464 72657373 313c2f6b 65793e0a 09093c73 7472696e 673e436f 6d70616e 79204164 64726573 733c2f73 7472696e 673e0a09 093c6b65 793e636f 6d70616e 795f6e61 6d653c2f 6b65793e 0a09093c 73747269 6e673e4e 65772053 75727665 7920436f 6d70616e 793c2f73 7472696e 673e0a09 3c2f6469 63743e0a 3c2f6469 63743e0a 3c2f706c 6973743e 0a> 

然而,這是我來的時候轉換這回該我得到的問題屬性列表:

所以,我選擇用我的數據:在sqlite3_column_blob

SELECT surveydata FROM surveys WHERE id = 2 

的原始數據(語句,0)是:

NSString *blobRaw = [NSString stringWithFormat:@"%s", sqlite3_column_blob(stmt, 0)]; 
NSLog(@"Raw BLOB = %@", blobRaw); 

Raw BLOB = <3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f 64696e67 3d225554 462d3822 3f3e0a3c 21444f43 54595045 20706c69 73742050 55424c49 4320222d 2f2f4170 706c652f 2f445444 20504c49 53542031 2e302f2f 454e2220 22687474 703a2f2f 7777772e 6170706c 652e636f 6d2f4454 44732f50 726f7065 7274794c 6973742d 312e302e 64746422 3e0a3c70 6c697374 20766572 73696f6e 3d22312e 30223e0a 3c646963 743e0a09 3c6b6579 3e636f6d 70616e79 64657461 696c733c 2f6b6579 3e0a093c 64696374 3e0a0909 3c6b6579 3e616464 72657373 313c2f6b 65793e0a 09093c73 7472696e 673e436f 6d70616e 79204164 64726573 733c2f73 7472696e 673e0a09 093c6b65 793e636f 6d70616e 795f6e61 6d653c2f 6b65793e 0a09093c 73747269 6e673e4e 65772053 75727665 7920436f 6d70616e 793c2f73 7472696e 673e0a09 3c2f6469 63743e0a 3c2f6469 63743e0a 3c2f706c 6973743e 0a> 

(NB:我只是選擇所述一個字段,以便它在列0)

[NSPropertyListSerialisation propertyListFromData]預計的NSData作爲輸入這樣我試圖團塊投射到與NSData的:

NSData *blobData = [NSData dataWithBytes:sqlite3_column_blob(stmt, 0) length:sqlite3_column_bytes(stmt, 0)]; 

但是,如果我輸出NSData的我得到:

<3c336333 66373836 64203663 32303736 36352037 32373336 39366620 36653364 32323331 20326533 30323232 30203635 36653633 36662036 34363936 65363720 33643232 35353534 20343632 64333832 32203366 33653061 33632032 31343434 66343320 35343539 35303435 20323037 30366336 39203733 37343230 35302035 35343234 63343920 34333230 32323264 20326632 66343137 30203730 36633635 32662032 66343435 34343420 32303530 34633439 20353335 34323033 31203265 33303266 32662034 35346532 32323020 32323638 37343734 20373033 61326632 66203737 37373737 32652036 31373037 30366320 36353265 36333666 20366432 66343435 34203434 37333266 35302037 32366637 30363520 37323734 37393463 20363937 33373432 64203331 32653330 32652036 34373436 34323220 33653061 33633730 20366336 39373337 34203230 37363635 37322037 33363936 66366520 33643232 33313265 20333032 32336530 61203363 36343639 36332037 34336530 61303920 33633662 36353739 20336536 33366636 64203730 36313665 37392036 34363537 34363120 36393663 37333363 20326636 62363537 39203365 30613039 33632036 34363936 33373420 33653061 30393039 20336336 62363537 39203365 36313634 36342037 32363537 33373320 33313363 32663662 20363537 39336530 61203039 30393363 37332037 34373236 39366520 36373365 34333666 20366437 30363136 65203739 32303431 36342036 34373236 35373320 37333363 32663733 20373437 32363936 65203637 33653061 30392030 39336336 62363520 37393365 36333666 20366437 30363136 65203739 35663665 36312036 64363533 63326620 36623635 37393365 20306130 39303933 63203733 37343732 36392036 65363733 65346520 36353737 32303533 20373537 32373636 35203739 32303433 36662036 64373036 31366520 37393363 32663733 20373437 32363936 65203637 33653061 30392033 63326636 34363920 36333734 33653061 20336332 66363436 39203633 37343365 30612033 63326637 30366320 36393733 37343365 2030613e> 

奇怪的是,如果我再通過[NSPropertySerialization propertyListFromData]這個NSData的獲取:

<3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f 64696e67 3d225554 462d3822 3f3e0a3c 21444f43 54595045 20706c69 73742050 55424c49 4320222d 2f2f4170 706c652f 2f445444 20504c49 53542031 2e302f2f 454e2220 22687474 703a2f2f 7777772e 6170706c 652e636f 6d2f4454 44732f50 726f7065 7274794c 6973742d 312e302e 64746422 3e0a3c70 6c697374 20766572 73696f6e 3d22312e 30223e0a 3c646963 743e0a09 3c6b6579 3e636f6d 70616e79 64657461 696c733c 2f6b6579 3e0a093c 64696374 3e0a0909 3c6b6579 3e616464 72657373 313c2f6b 65793e0a 09093c73 7472696e 673e436f 6d70616e 79204164 64726573 733c2f73 7472696e 673e0a09 093c6b65 793e636f 6d70616e 795f6e61 6d653c2f 6b65793e 0a09093c 73747269 6e673e4e 65772053 75727665 7920436f 6d70616e 793c2f73 7472696e 673e0a09 3c2f6469 63743e0a 3c2f6469 63743e0a 3c2f706c 6973743e 0a> 

這與序列化輸入和blob數據相同。

請任何人都可以幫助我如何存儲序列化的屬性列表,並從sqlite表中恢復它 - BLOB似乎是正確的方式來存儲數據,但我不能重構它。

回答

2

通常,blob數據類型不適合只插入引號內的SQL語句。因此,而不是你的:

NSString *insSQL = [NSString stringWithFormat:@"INSERT INTO surveys (company_name, address, surveydata) VALUES (\"%@\", \"%@\", \"%@\")", SURVEY_DEFAULT_COMPANY, SURVEY_DEFAULT_ADDRESS, data]; 

你可能要考慮:

NSString *insSQL = @"INSERT INTO surveys (company_name, address, surveydata) VALUES (?, ?, ?)"; 
if (sqlite3_bind_text(database, 1, [SURVEY_DEFAULT_COMPANY UTF8String]) != SQLITE_OK) 
    NSLog("Bind error on #%d: '%s'", 1, sqlite3_errmsg(database)); 
if (sqlite3_bind_text(database, 2, [SURVEY_DEFAULT_ADDRESS UTF8String]) != SQLITE_OK) 
    NSLog("Bind error on #%d: '%s'", 2, sqlite3_errmsg(database)); 
if (sqlite3_bind_blob(database, 3, [data bytes], [data length], SQLITE_STATIC) != SQLITE_OK) 
    NSLog("Bind error on #%d: '%s'", 3, sqlite3_errmsg(database)); 

我沒有測試上面的代碼,但希望你的想法。不要自己構建SQL語句,而應使用sqlite3_bind functions


這裏有一個如何序列化NSDictionary,存放於數據庫中,然後檢索和反序列化一個更完整的例子。這裏是你如何序​​列化和保存在SQLite數據庫中的數據:

- (IBAction)saveButtonClicked:(id)sender 
{ 
    sqlite3 *database; 

    NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; 
    NSString *databaseName = [documentsDirectory stringByAppendingPathComponent:@"test.db"]; 

    if (sqlite3_open([databaseName UTF8String], &database) != SQLITE_OK) 
    { 
     NSLog(@"open failed"); 
     return; 
    } 

    NSString *sql = @"CREATE TABLE IF NOT EXISTS company (company_id INTEGER PRIMARY KEY AUTOINCREMENT, company_details BLOB);"; 

    if (sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL) != SQLITE_OK) 
    { 
     NSLog(@"create failed %s", sqlite3_errmsg(database)); 
     sqlite3_close(database); 
     return; 
    } 

    NSDictionary *dictionary = @{@"name" : @"IBM", 
           @"street" : @"1 New Orchard Road", 
           @"city" : @"Armonk", 
           @"state" : @"New York", 
           @"postal" : @"10504-1722"}; 

    NSData *data= [NSPropertyListSerialization dataFromPropertyList:dictionary 
                  format:NSPropertyListXMLFormat_v1_0 
                errorDescription:nil]; 

    sql = @"INSERT INTO company (company_details) VALUES (?);"; 
    sqlite3_stmt *statement; 

    if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, NULL) != SQLITE_OK) 
    { 
     NSLog(@"prepare failed %s", sqlite3_errmsg(database)); 
     sqlite3_close(database); 
     return; 
    } 

    if (sqlite3_bind_blob(statement, 1, [data bytes], [data length], SQLITE_STATIC) != SQLITE_OK) 
    { 
     NSLog(@"bind failed %s", sqlite3_errmsg(database)); 
     sqlite3_finalize(statement); 
     sqlite3_close(database); 
     return; 
    } 

    if (sqlite3_step(statement) != SQLITE_DONE) 
    { 
     NSLog(@"step failed %s", sqlite3_errmsg(database)); 
    } 
    else 
    { 
     NSLog(@"success"); 
    } 

    sqlite3_finalize(statement); 

    sqlite3_close(database); 
} 

以及加載和反序列化數據:

- (IBAction)loadButtonClicked:(id)sender 
{ 
    sqlite3 *database; 

    NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; 
    NSString *databaseName = [documentsDirectory stringByAppendingPathComponent:@"test.db"]; 

    if (sqlite3_open([databaseName UTF8String], &database) != SQLITE_OK) 
    { 
     NSLog(@"open failed"); 
     return; 
    } 

    sqlite3_stmt *statement; 
    NSString *sql = @"SELECT * FROM company;"; 

    if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, NULL) != SQLITE_OK) 
    { 
     NSLog(@"prepare failed %s", sqlite3_errmsg(database)); 
     return; 
    } 

    while (sqlite3_step(statement) == SQLITE_ROW) 
    { 
     NSInteger companyId = sqlite3_column_int(statement, 0); 
     const void *blob = sqlite3_column_blob(statement, 1); 
     NSInteger bytes = sqlite3_column_bytes(statement, 1); 
     NSData *data = [NSData dataWithBytes:blob length:bytes]; 

     NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:data 
                    mutabilityOption:NSPropertyListImmutable 
                       format:NULL 
                    errorDescription:nil]; 

     NSLog(@"%@", @{@"company_id"  : @(companyId), 
         @"company_details" : dictionary}); 
    } 

    sqlite3_finalize(statement); 

    sqlite3_close(database); 
} 
1

你把包含BLOB的ASCII表示成一個字符串數據庫:

INSERT INTO surveys(...) VALUES(..., "<3c3f786d...") 

然而,SQLite中,一個blob literal必須格式化爲一個單引號字符串以x前綴,並且必須包含像<>任何其他字符:

INSERT INTO surveys(...) VALUES(..., x'3c3f786d...') 

總之,爲了避免格式化這樣的問題,使用像在羅布的回答是首選參數。

+0

非常感謝,我選擇了Rob的答案,我個人更喜歡這個,因爲它消除了數據類型中的所有歧義。我分開閱讀答案,只要我看到「綁定」,我就明白自己出錯的地方。 – JamesB