苹果配置中大量用到了plist,使用开源的C语言的库libplist可以读取解析,但是纯C的写起来非常蛋疼。
于是用C++封装了一下(c++ wrapper),但是plist的本质还是xml,读取起来还是略有繁琐,于是转为json结构,就可以直接使用了(依赖jsoncpp库)。
plist内部是有PLIST_UID、PLIST_DICT、PLIST_ARRAY等复杂的结构,支持xml和binary(bplist)两种格式,支持uid自动解析处理关联,支持NS.objects、NS.keys、NSDictionary、NSArray、NSMutableDictionary、NSMutableArray等结构自动处理,使用起来就非常方便了。
header only引用:plistObject.h
#pragma once #include <json/json.h> #include <plist/plist++.h> #include "base64.h" //屏蔽掉调试输出 //#define _bplist_printf class CPlistObject : public Json::Value { public: template <class T> static Json::Value Plist2Json(const T& filepath) { CPlistObject plist; if (plist.OpenPlistFile(filepath)) return std::move(plist); return {}; } static Json::Value PlistBuf2Json(const std::string &buf) { CPlistObject plist; if (plist.OpenPlistBuf(buf)) return std::move(plist); return {}; } CPlistObject(bool bConvertDataPlist = false) { m_plistMain = NULL; m_pArray = NULL; m_bConvertDataPlist = bConvertDataPlist; } ~CPlistObject() { Close(); } void Close() { if (m_plistMain) { plist_free(m_plistMain); m_plistMain = NULL; } } //打开plist内存缓冲区 bool OpenPlistBuf(const void* lpBuffer, size_t nBufLen) { if (memcmp(lpBuffer, "bplist00", 8) == 0) plist_from_bin((const char*)lpBuffer, (uint32_t)nBufLen, &m_plistMain); else //if (memcmp(lpBuffer, "<?xml", 5) == 0) plist_from_xml((const char*)lpBuffer, (uint32_t)nBufLen, &m_plistMain); if (!m_plistMain) return false; if (!_ConvertPlistObject(*this)) return false; return true; } bool OpenPlistBuf(const std::string& buf) { return OpenPlistBuf(buf.c_str(), buf.length()); } //打开plist文件 template <class T> bool OpenPlistFile(const T& filepath) { std::ifstream ifs(filepath, std::ios::binary); if (!ifs.is_open()) return false; unsigned char header[4] = { 0 }; ifs.read((char*)header, 3); //兼容win utf8 bom 0xEFBBBF if (header[0] == 0xEF && header[1] == 0xBB && header[2] == 0xBF) { //貌似不需要处理啥 } else { //指针回到开头 ifs.seekg(0, std::ios::beg); } //读取文件内容 std::ostringstream sstr; sstr << ifs.rdbuf(); std::string buf = sstr.str(); //读文件失败了 if (buf.empty()) return false; return OpenPlistBuf(buf.c_str(), buf.length()); } //转换plist为json // bool ConvertPlistObject(bool bConvertDataPlist = false) // { // if (bConvertDataPlist) // return _ConvertPlistObject(m_jsonValues); // } private: bool _ConvertPlistObject(Json::Value& jsonObject) { m_pArray = plist_dict_get_item(m_plistMain, "$objects"); if (m_pArray && plist_get_node_type(m_pArray) == PLIST_ARRAY) { int nArrayCount = plist_array_get_size(m_pArray); for (int i = 0; i < nArrayCount; i++) { plist_t pItem = plist_array_get_item(m_pArray, i); plist_type nType = plist_get_node_type(pItem); if (nType == PLIST_DICT) { ConvertDict(pItem, jsonObject); return (!jsonObject.isNull()); } } } int nType = plist_get_node_type(m_plistMain); if (nType == PLIST_DICT) { EnumDictKeyValueDefault(m_plistMain, jsonObject); } else if (nType == PLIST_ARRAY) { EnumArray(m_plistMain, jsonObject); } else { SetValue(m_plistMain, jsonObject); } return (!jsonObject.isNull()); } plist_t GetNodeByUidDict(plist_t pDict) { if (!pDict) return NULL; plist_type nType = plist_get_node_type(pDict); //如果类型不是UID的 直接就返回原元素指针 if (nType != PLIST_UID) return pDict; plist_t pRet = NULL; uint64_t val = 0; plist_get_uid_val(pDict, &val); pRet = plist_array_get_item(m_pArray, (uint32_t)val); return pRet; } std::string GetDictClassName(plist_t pDict) { std::string strRet; plist_t pClassName = GetNodeByUidDict(pDict); if (pClassName) { if (plist_get_node_type(pClassName) == PLIST_DICT) { plist_t pName = plist_dict_get_item(pClassName, "$classname"); if (pName) { uint64_t sLen = 0; const char* pszName = plist_get_string_ptr(pName, &sLen); if (pszName) strRet = pszName; } } } return strRet; } void SetValue(plist_t pValue, Json::Value& jsonValue) { int nType = plist_get_node_type(pValue); switch (nType) { case PLIST_BOOLEAN: { uint8_t nValue8 = 0; plist_get_bool_val(pValue, &nValue8); //_bplist_printf("val(bool):%d \n", nValue8); jsonValue = nValue8; break; } case PLIST_UINT: { uint64_t nValue64 = 0; plist_get_uint_val(pValue, (uint64_t*)&nValue64); //_bplist_printf("val(uint):%llu \n", nValue64); jsonValue = nValue64; break; } case PLIST_REAL: { double dValue = 0; plist_get_real_val(pValue, &dValue); //_bplist_printf("val(real):%f \n", dValue); jsonValue = dValue; break; } case PLIST_STRING: { uint64_t sLen = 0; const char* pszStr = plist_get_string_ptr(pValue, &sLen); if (pszStr) jsonValue = pszStr; else jsonValue = ""; break; } case PLIST_DATE: { int32_t nSec, nMSec; plist_get_date_val(pValue, &nSec, &nMSec); uint32_t dwSec = nSec + 978278400; //Represents the number of seconds since 01/01/2001 jsonValue = dwSec; break; } case PLIST_DATA: { uint64_t nDataLen = 0; const char* pszData = plist_get_data_ptr(pValue, &nDataLen); if (nDataLen > 0) { //add by fenlog 2019.11.07 转义data中的plist if (m_bConvertDataPlist && nDataLen > 16 && (memcmp(pszData, "bplist00", 8) == 0 || memcmp(pszData, "<?xml", 5) == 0)) { CPlistObject mSubPlist(true); if (mSubPlist.OpenPlistBuf(pszData, (size_t)nDataLen)) { jsonValue = mSubPlist; return; } } } jsonValue = base64_encode((const unsigned char*)pszData, (size_t)nDataLen); break; } default: break; } } bool CheckItemType(plist_t pNode, plist_type nType) { if (pNode && plist_get_node_type(pNode) == nType) return true; return false; } void EnumDictNSMutableArray(plist_t pDictObject, Json::Value& jsonValue) { plist_t pNSObjects = plist_dict_get_item(pDictObject, "NS.objects"); if (CheckItemType(pNSObjects, PLIST_ARRAY)) { uint32_t nObjects = plist_array_get_size(pNSObjects); for (uint32_t i = 0; i < nObjects; i++) { plist_t pValue = GetNodeByUidDict(plist_array_get_item(pNSObjects, i)); if (pValue) { plist_type nType = plist_get_node_type(pValue); //_bplist_printf("%d - type %d ", i, nType); if (nType == PLIST_DICT) { //_bplist_printf("\n"); ConvertDict(pValue, jsonValue[i]); } else { SetValue(pValue, jsonValue[i]); } } } } } void EnumDictNSMutableDictionary(plist_t pDictObject, Json::Value& jsonValue) { plist_t pNSKeys = plist_dict_get_item(pDictObject, "NS.keys"); plist_t pNSObjects = plist_dict_get_item(pDictObject, "NS.objects"); if (CheckItemType(pNSKeys, PLIST_ARRAY) && CheckItemType(pNSObjects, PLIST_ARRAY)) { uint32_t nKeys = plist_array_get_size(pNSKeys); if (plist_array_get_size(pNSObjects) == nKeys) { for (uint32_t i = 0; i < nKeys; i++) { plist_t pKey = GetNodeByUidDict(plist_array_get_item(pNSKeys, i)); plist_t pValue = GetNodeByUidDict(plist_array_get_item(pNSObjects, i)); if (pKey && pValue) { //modify by fenlog 2019.07.18 plist_type nKeyType = plist_get_node_type(pKey); plist_type nValueType = plist_get_node_type(pValue); if (nKeyType == PLIST_STRING) { uint64_t sLen = 0; const char* pszKey = plist_get_string_ptr(pKey, &sLen); if (pszKey) { //_bplist_printf("%s - type %d ", pszKey, nValueType); if (nValueType == PLIST_DICT) { //_bplist_printf("\n"); ConvertDict(pValue, jsonValue[pszKey]); } else { SetValue(pValue, jsonValue[pszKey]); } } } else if (nKeyType == PLIST_UINT) { uint64_t nKeyValue = 0; plist_get_uint_val(pKey, &nKeyValue); //_bplist_printf("%llu - type %d ", nKeyValue, nValueType); if (nValueType == PLIST_DICT) { //_bplist_printf("\n"); ConvertDict(pValue, jsonValue[std::to_string(nKeyValue)]); } else { SetValue(pValue, jsonValue[std::to_string(nKeyValue)]); } } else if (nKeyType == PLIST_DICT) { std::string index = std::to_string(i); ConvertDict(pKey, jsonValue[index]["key"]); if (nValueType == PLIST_DICT) ConvertDict(pValue, jsonValue[index]["value"]); else SetValue(pValue, jsonValue[index]["value"]); } else { //_bplist_printf("unknow type %d \n", nKeyType); } } } } } } void EnumArray(plist_t pArrObject, Json::Value& jsonValue) { plist_type nType = plist_get_node_type(pArrObject); if (nType != PLIST_ARRAY) return; uint32_t nArrayCount = plist_array_get_size(pArrObject); for (uint32_t i = 0; i < nArrayCount; i++) { plist_t pItem = plist_array_get_item(pArrObject, i); nType = plist_get_node_type(pItem); if (nType == PLIST_DICT) { EnumDictKeyValueDefault(pItem, jsonValue[i]); } else if (nType == PLIST_ARRAY) { EnumArray(pItem, jsonValue[i]); } else { SetValue(pItem, jsonValue[i]); } } } void EnumDictKeyValueDefault(plist_t pDictObject, Json::Value& jsonValue) { plist_dict_iter it = NULL; char* key = NULL; plist_t subnode = NULL; plist_dict_new_iter(pDictObject, &it); plist_dict_next_item(pDictObject, it, &key, &subnode); while (subnode) { int nType = plist_get_node_type(subnode); //UID的重新修正目标指针 if (nType == PLIST_UID) { subnode = GetNodeByUidDict(subnode); //重新读取type if (subnode) nType = plist_get_node_type(subnode); } //_bplist_printf("%s - type %d \n", key, nType); if (nType == PLIST_DICT) { //_bplist_printf("\n"); ConvertDict(subnode, jsonValue[key]); } else if (nType == PLIST_ARRAY) { EnumArray(subnode, jsonValue[key]); } else { SetValue(subnode, jsonValue[key]); } subnode = NULL; plist_to_bin_free(key); //没有plist_mem_free,临时用这个代替 key = NULL; plist_dict_next_item(pDictObject, it, &key, &subnode); } plist_to_bin_free((char*)it);//没有plist_mem_free,临时用这个代替 } void ConvertDict(plist_t pDict, Json::Value& jsonValue) { //如果是dict 可能是对象描述 plist_t pDictObject = plist_dict_get_item(pDict, "$class"); if (pDictObject) { std::string strClassName = GetDictClassName(pDictObject); //一般这里都不会为空 if (!strClassName.empty()) { //_bplist_printf("Get Class:%s \n", strClassName.c_str()); if (strClassName == "NSMutableDictionary" || strClassName == "NSDictionary") { EnumDictNSMutableDictionary(pDict, jsonValue); } else if (strClassName == "NSMutableArray" || strClassName == "NSArray") { EnumDictNSMutableArray(pDict, jsonValue); } else { //遍历里面的key-value EnumDictKeyValueDefault(pDict, jsonValue); } } } else { EnumDictKeyValueDefault(pDict, jsonValue); } } std::string ConvertPlistToXML(plist_t plist = NULL) { std::string strRet; if (!plist) { if (!m_plistMain) return strRet; plist = m_plistMain; } char* pszMsg = NULL; uint32_t nMsgLen = 0; plist_to_xml(plist, &pszMsg, &nMsgLen); //printf("%s \n", pszMsg); if (pszMsg) { strRet = pszMsg; plist_to_xml_free(pszMsg); } return strRet; } plist_t m_plistMain; plist_t m_pArray; bool m_bConvertDataPlist; };
比如一个示例的plist:demo.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>$version</key> <integer>100000</integer> <key>$archiver</key> <string>NSKeyedArchiver</string> <key>$top</key> <dict> <key>root</key> <dict> <key>CF$UID</key> <integer>1</integer> </dict> </dict> <key>$objects</key> <array> <string>$null</string> <dict> <key>_closeLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_intimateRelationDark</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_sex</key> <dict> <key>CF$UID</key> <integer>9</integer> </dict> <key>_gameLastLoginTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_closeDays</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_chatHotLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_isFriendDontDisturb</key> <dict> <key>CF$UID</key> <integer>10</integer> </dict> <key>_magicFontOpenFlagUpdateTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_onlineMusicRemainingTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_needShowFriendTreeAnimation</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_onlineMusicEndTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_interactiveSignMode</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_friendTreeLastChatTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_richOnlineState</key> <dict> <key>CF$UID</key> <integer>5</integer> </dict> <key>_ctType</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_needShipAnimation</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_friendTreePreLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_vipInfoDic</key> <dict> <key>CF$UID</key> <integer>15</integer> </dict> <key>_intimateRelationFlag</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_network</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_lastGetCustomStateTime</key> <dict> <key>CF$UID</key> <integer>20</integer> </dict> <key>_enableQSL</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_QQRobert</key> <dict> <key>CF$UID</key> <integer>3</integer> </dict> <key>_avatarIdUpdateTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_isMatchRealNick</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_shouldShowCustomState</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_status</key> <dict> <key>CF$UID</key> <integer>6</integer> </dict> <key>_magicFontType</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_cSpecialFlag</key> <dict> <key>CF$UID</key> <integer>3</integer> </dict> <key>_specialCareZone</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_qzoneLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_gameAppId</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_createTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_nameplateVipType</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_eIconType</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_isShortcutEnable</key> <dict> <key>CF$UID</key> <integer>2</integer> </dict> <key>_memberLevel</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_isFriendMask</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_isMatchUIN</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_friendTreeFlag</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_loverKeyFlag</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_age</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_fuin</key> <dict> <key>CF$UID</key> <integer>13</integer> </dict> <key>_abiFlag</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_priority</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_locationAbility</key> <dict> <key>CF$UID</key> <integer>3</integer> </dict> <key>_matchRange</key> <dict> <key>CF$UID</key> <integer>21</integer> </dict> <key>_loverKeyLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_intimateRelationLastChatTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_topIdentityInfo</key> <dict> <key>CF$UID</key> <integer>10</integer> </dict> <key>_fontIdUpdateTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_lastGetOnlineInfoTime</key> <dict> <key>CF$UID</key> <integer>18</integer> </dict> <key>_isEnglishName</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_qslCreateTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_loverKeyTransFlag</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_gameNameplateId</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_notiNoVibrate</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_enableYQHType</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_praiseHotLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_lastKeyTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_praiseHotDays</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_loverFlag</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_avatarId</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_newBoatLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_groupId</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_lastLoginClient</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_extendNameplateId</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_faceAddonId</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_isBlockedMask</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_onlineMusicSourceType</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_lastPraiseTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_colorRingId</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_isShortcutEnableUpdate</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_isBlockMask</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_dstUin</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_terminalDescription</key> <dict> <key>CF$UID</key> <integer>17</integer> </dict> <key>_customStateDescription</key> <dict> <key>CF$UID</key> <integer>17</integer> </dict> <key>_gameRank</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_intimateRelationLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_friendTreeContinuityDays</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_qzoneDays</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_isRemark</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_holdFlag</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_grayNameplateFlag</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>$class</key> <dict> <key>CF$UID</key> <integer>23</integer> </dict> <key>_notiNeedHideSession</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_isFriendSetTop</key> <dict> <key>CF$UID</key> <integer>10</integer> </dict> <key>_vipFontId</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_nick</key> <dict> <key>CF$UID</key> <integer>12</integer> </dict> <key>_lastChatTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_onlineMusicTotalTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_isSpecialCareOpen</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_pendantIdUpdateTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_head</key> <dict> <key>CF$UID</key> <integer>8</integer> </dict> <key>_sqq</key> <dict> <key>CF$UID</key> <integer>3</integer> </dict> <key>_isMsfUser</key> <dict> <key>CF$UID</key> <integer>3</integer> </dict> <key>_maxGetCustomStateInterval</key> <dict> <key>CF$UID</key> <integer>19</integer> </dict> <key>_isQimFriend</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_chatHotDays</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_magicFontTypeUpdateTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_supportVideo</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_isWifi</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_notiNoSound</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_lastQzoneTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_recommendHoldFlag</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_friendTreeNotifyTime</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_bNotShowNetWorkIcon</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_gameIconShowFlag</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_notiNoPreview</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>_maxGetOnlineInfoInterval</key> <dict> <key>CF$UID</key> <integer>19</integer> </dict> <key>_lastLoginType</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>_newBoatDays</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_friendTreeLevel</key> <dict> <key>CF$UID</key> <integer>11</integer> </dict> <key>_sig</key> <dict> <key>CF$UID</key> <integer>14</integer> </dict> <key>_propertyMD5</key> <dict> <key>CF$UID</key> <integer>22</integer> </dict> </dict> <true/> <integer>0</integer> <false/> <integer>65535</integer> <integer>20</integer> <integer>0</integer> <integer>201</integer> <integer>2</integer> <integer>-1</integer> <integer>0</integer> <string>10001</string> <string>1000194</string> <string>U</string> <dict> <key>NS.keys</key> <array> </array> <key>NS.objects</key> <array> </array> <key>$class</key> <dict> <key>CF$UID</key> <integer>16</integer> </dict> </dict> <dict> <key>$classname</key> <string>NSDictionary</string> <key>$classes</key> <array> <string>NSDictionary</string> <string>NSObject</string> </array> </dict> <string></string> <real>1682211584.541188</real> <real>0.000000</real> <real>1682211584.541188</real> <data> AAAAAAAAAAAAAAAAAAAAAA== </data> <string></string> <dict> <key>$classname</key> <string>QQFriendModel</string> <key>$classes</key> <array> <string>QQFriendModel</string> <string>QQModel</string> <string>NSObject</string> </array> </dict> </array> </dict> </plist>
可以看到里面有很多CF$UID这种UID类型的key值需要重新匹配具体内容,还涉及到NSDictionary等对象,通过类解析后的json结构是:
{ "$class" : { "$classes" : [ "QQFriendModel", "QQModel", "NSObject" ], "$classname" : "QQFriendModel" }, "_QQRobert" : 0, "_abiFlag" : 0, "_age" : 0, "_avatarId" : 0, "_avatarIdUpdateTime" : 0, "_bNotShowNetWorkIcon" : 0, "_cSpecialFlag" : 0, "_chatHotDays" : 0, "_chatHotLevel" : 0, "_closeDays" : 0, "_closeLevel" : 0, "_colorRingId" : 0, "_createTime" : 0, "_ctType" : 0, "_customStateDescription" : "", "_dstUin" : 0, "_eIconType" : 0, "_enableQSL" : 0, "_enableYQHType" : 0, "_extendNameplateId" : 0, "_faceAddonId" : 0, "_fontIdUpdateTime" : 0, "_friendTreeContinuityDays" : 0, "_friendTreeFlag" : 0, "_friendTreeLastChatTime" : 0, "_friendTreeLevel" : 0, "_friendTreeNotifyTime" : 0, "_friendTreePreLevel" : 0, "_fuin" : "1000194", "_gameAppId" : 0, "_gameIconShowFlag" : 0, "_gameLastLoginTime" : 0, "_gameNameplateId" : 0, "_gameRank" : 0, "_grayNameplateFlag" : 0, "_groupId" : 0, "_head" : 201, "_holdFlag" : 0, "_interactiveSignMode" : 0, "_intimateRelationDark" : 0, "_intimateRelationFlag" : 0, "_intimateRelationLastChatTime" : 0, "_intimateRelationLevel" : 0, "_isBlockMask" : 0, "_isBlockedMask" : 0, "_isEnglishName" : 0, "_isFriendDontDisturb" : 18446744073709551615, "_isFriendMask" : 0, "_isFriendSetTop" : 18446744073709551615, "_isMatchRealNick" : 0, "_isMatchUIN" : 0, "_isMsfUser" : 0, "_isQimFriend" : 0, "_isRemark" : 0, "_isShortcutEnable" : 1, "_isShortcutEnableUpdate" : 0, "_isSpecialCareOpen" : 0, "_isWifi" : 0, "_lastChatTime" : 0, "_lastGetCustomStateTime" : 1682211584.541188, "_lastGetOnlineInfoTime" : 1682211584.541188, "_lastKeyTime" : 0, "_lastLoginClient" : 0, "_lastLoginType" : 0, "_lastPraiseTime" : 0, "_lastQzoneTime" : 0, "_locationAbility" : 0, "_loverFlag" : 0, "_loverKeyFlag" : 0, "_loverKeyLevel" : 0, "_loverKeyTransFlag" : 0, "_magicFontOpenFlagUpdateTime" : 0, "_magicFontType" : 0, "_magicFontTypeUpdateTime" : 0, "_matchRange" : "AAAAAAAAAAAAAAAAAAAAAA==", "_maxGetCustomStateInterval" : 0.0, "_maxGetOnlineInfoInterval" : 0.0, "_memberLevel" : 0, "_nameplateVipType" : 0, "_needShipAnimation" : 0, "_needShowFriendTreeAnimation" : 0, "_network" : 0, "_newBoatDays" : 0, "_newBoatLevel" : 0, "_nick" : "10001", "_notiNeedHideSession" : 0, "_notiNoPreview" : 0, "_notiNoSound" : 0, "_notiNoVibrate" : 0, "_onlineMusicEndTime" : 0, "_onlineMusicRemainingTime" : 0, "_onlineMusicSourceType" : 0, "_onlineMusicTotalTime" : 0, "_pendantIdUpdateTime" : 0, "_praiseHotDays" : 0, "_praiseHotLevel" : 0, "_priority" : 0, "_propertyMD5" : "", "_qslCreateTime" : 0, "_qzoneDays" : 0, "_qzoneLevel" : 0, "_recommendHoldFlag" : 0, "_richOnlineState" : 65535, "_sex" : 2, "_shouldShowCustomState" : 0, "_sig" : "U", "_specialCareZone" : 0, "_sqq" : 0, "_status" : 20, "_supportVideo" : 0, "_terminalDescription" : "", "_topIdentityInfo" : 18446744073709551615, "_vipFontId" : 0, "_vipInfoDic" : null }
比如需要解析并读取"_richOnlineState"字段的值,解析示例代码:
CPlistObject mPlist; mPlist.OpenPlistFile("demo.plist"); uint32_t v = mPlist["_richOnlineState"].asInt();
嗯,非常简单。最后读取的时候,记得校验字段是否存在,否则jsoncpp可能会抛异常。