BlackFeather'S Blog 我的技术小博 -- C/C++,Python,Golang

plist转json解析类plist2json(libplist C++封装类)


苹果配置中大量用到了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可能会抛异常。




2023年12月27日 | 发布:blackfeather | 分类:C/C++代码 | 评论:0

发表留言: