苹果plist转json

C/C++代码 blackfeather

plist是苹果最常用的配置文件存储类型之一,plist实际为xml,xml操作远没有json方便,并且苹果很多应用存储的plist跟NSArray等对象是绑定的,又会设计到了uid重定位等问题,于是写了一个plist转json的类,一次convert,方便受用。

内部基本上都封装完毕,基于libplist和jsoncpp库,支持xml和bplist两种格式,data类型为了考虑十六进制buffer使用了base64编码


使用方法:

CPlistObject mPlistHandler;
mPlistHandler.OpenPlist("xxxx.plist");  //也支持从数据流中加载OpenPlist(LPCSTR lpBuffer, int nBufLen);

//bConvertDataPlist参数默认为FALSE,  TRUE的作用为检测data类型的数据,如果其中的数据是plist的buffer,则再次进行解析(参考代码132行)
Json::Value jsonPlist;
mPlistHandler.ConvertPlistObject(jsonPlist); 
//操作值
std::string str = jsonPlist["xxx"].asString();




源码:

BPlist.cpp

#include "stdafx.h"
#include "BPlist.h"
#include "base64.h"

// #ifdef _DEBUG
// #pragma comment(lib, "libiconv_d.lib")
// #pragma comment(lib, "libxml2_d.lib")
// #pragma comment(lib, "libcnary_d.lib")
// #pragma comment(lib, "libplist_d.lib")
// #else
// #pragma comment(lib, "libiconv.lib")
// #pragma comment(lib, "libxml2.lib")
// #pragma comment(lib, "libcnary.lib")
// #pragma comment(lib, "libplist.lib")
// #endif // _DEBUG

#pragma comment(lib, "libiconv.lib")
#pragma comment(lib, "libxml2.lib")
#pragma comment(lib, "libcnary.lib")
#pragma comment(lib, "libplist.lib")

//屏蔽掉调试输出
#define myprintf


CPlistObject::CPlistObject()
{
	m_plistMain = NULL;
	m_pArray = NULL;
	m_bConvertDataPlist = FALSE;
}


CPlistObject::~CPlistObject()
{
	Close();
}

void CPlistObject::Close()
{
	if (m_plistMain)
	{
		plist_free(m_plistMain);
		m_plistMain = NULL;
	}
}


plist_t CPlistObject::GetNodeByUidDict(plist_t pDict)
{
	if (!pDict)
		return NULL;

	int 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)val);

	return pRet;
}

CStringA CPlistObject::GetDictClassName(plist_t pDict)
{
	CStringA 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)
			{
				char *pszName = NULL;
				plist_get_string_val(pName, &pszName);
				if (pszName)
				{
					strRet = pszName;
					free(pszName);
				}
			}
		}
	}

	return strRet;
}


void CPlistObject::SetValue(plist_t pValue, Json::Value &jsonValue)
{
	int nType = plist_get_node_type(pValue);
	if (nType == PLIST_STRING)
	{
		char *pszValue = NULL;
		plist_get_string_val(pValue, &pszValue);
		if (pszValue)
		{
			myprintf("val(str):%s \n", pszValue);
			jsonValue = pszValue;
			free(pszValue);
		}
	}
	else if (nType == PLIST_UINT)
	{
		UINT64 nValue;
		plist_get_uint_val(pValue, &nValue);
		myprintf("val(uint):%I64d \n", nValue);
		jsonValue = nValue;
	}
	else if (nType == PLIST_BOOLEAN)
	{
		UINT8 nValue;
		plist_get_bool_val(pValue, &nValue);
		myprintf("val(bool):%d \n", nValue);
		jsonValue = nValue;
	}
	else if (nType == PLIST_DATA)
	{
 		char *pszValue = NULL;
 		uint64_t nDataLen = 0;
 		plist_get_data_val(pValue, &pszValue, &nDataLen);

		std::string strValue;
		if (nDataLen > 0)
		{
			//add by fenlog 2019.11.07 转义data中的plist
			if (m_bConvertDataPlist && nDataLen > 16 && (memcmp(pszValue, "bplist00", 8) == 0 || memcmp(pszValue, "<?xml", 5) == 0))
			{
				CPlistObject mPlist;
				if (mPlist.OpenPlist(pszValue, (int)nDataLen) && mPlist.ConvertPlistObject(TRUE))
				{
					jsonValue = mPlist.m_jsonValues;
					free(pszValue);
					return;
				}
			}

			strValue = base64_encode((const unsigned char *)pszValue, (DWORD)nDataLen);
		}

		if (pszValue)
			free(pszValue);

		myprintf("val(data):%s \n", strValue.c_str());
		jsonValue = strValue;
	}
	else if (nType == PLIST_REAL)
	{
		double nValue;
		plist_get_real_val(pValue, &nValue);
		myprintf("val(real):%lf \n", nValue);
		jsonValue = nValue;
	}
	else if (nType == PLIST_DATE)
	{
		int nSec, nMSec;
		plist_get_date_val(pValue, &nSec, &nMSec);
		UINT dwSec = nSec + 978278400; //Represents the number of seconds since 01/01/2001
		jsonValue = dwSec;
	}
	else
	{
		myprintf("val(%d) \n", nType);
	}
}


BOOL CPlistObject::CheckItemType(plist_t pNode, plist_type nType)
{
	if (pNode && plist_get_node_type(pNode) == nType)
		return TRUE;

	return FALSE;
}


void CPlistObject::EnumDictNSMutableArray(plist_t pDictObject, Json::Value &jsonValue)
{
	plist_t pNSObjects = plist_dict_get_item(pDictObject, "NS.objects");
	if (CheckItemType(pNSObjects, PLIST_ARRAY))
	{
		int nObjects = plist_array_get_size(pNSObjects);
		for (int i = 0; i < nObjects; i++)
		{
			plist_t pValue = GetNodeByUidDict(plist_array_get_item(pNSObjects, i));
			if (pValue)
			{
				int nType = plist_get_node_type(pValue);
				myprintf("%d - type %d ", i, nType);

				if (nType == PLIST_DICT)
				{
					myprintf("\n");
					ConvertDict(pValue, jsonValue[i]);
				}
				else
				{
					SetValue(pValue, jsonValue[i]);
				}
			}
		}
	}
}

void CPlistObject::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))
	{
		int nKeys = plist_array_get_size(pNSKeys);
		if (plist_array_get_size(pNSObjects) == nKeys)
		{
			for (int 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
					int nKeyType = plist_get_node_type(pKey);
					int nValueType = plist_get_node_type(pValue);
					if (nKeyType == PLIST_STRING)
					{
						char *pszKey = NULL;
						plist_get_string_val(pKey, &pszKey);
						if (pszKey)
						{
							if (strlen(pszKey) > 0)
							{
								myprintf("%s - type %d ", pszKey, nValueType);

								if (nValueType == PLIST_DICT)
								{
									myprintf("\n");
									ConvertDict(pValue, jsonValue[pszKey]);
								}
								else
								{
									SetValue(pValue, jsonValue[pszKey]);
								}
							}

							free(pszKey);
						}
					}
					else if (nKeyType == PLIST_DICT)
					{
						char szKey[16];
						sprintf(szKey, "%u", i);
						ConvertDict(pKey, jsonValue[szKey]["key"]);
						if (nValueType == PLIST_DICT)
							ConvertDict(pValue, jsonValue[szKey]["value"]);
						else
							SetValue(pValue, jsonValue[szKey]["value"]);
					}
					else
					{
						myprintf("unknow type %d \n", nKeyType);
					}
				}
			}
		}
	}
}


void CPlistObject::EnumArray(plist_t pArrObject, Json::Value &jsonValue)
{
	int nType = plist_get_node_type(pArrObject);
	if (nType != PLIST_ARRAY)
		return;

	int nArrayCount = plist_array_get_size(pArrObject);
	for (int 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 CPlistObject::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);
		}

		myprintf("%s - type %d \n", key, nType);

		if (nType == PLIST_DICT)
		{
			myprintf("\n");
			ConvertDict(subnode, jsonValue[key]);
		}
		else if (nType == PLIST_ARRAY)
		{
			EnumArray(subnode, jsonValue[key]);
		}
		else
		{
			SetValue(subnode, jsonValue[key]);
		}

		subnode = NULL;
		free(key);
		key = NULL;
		plist_dict_next_item(pDictObject, it, &key, &subnode);
	}
	free(it);
}


void CPlistObject::ConvertDict(plist_t pDict, Json::Value &jsonValue)
{
	//如果是dict 可能是对象描述
	plist_t pDictObject = plist_dict_get_item(pDict, "$class");
	if (pDictObject)
	{
		CStringA strClassName = GetDictClassName(pDictObject);

		//一般这里都不会为空
		if (!strClassName.IsEmpty())
		{
			myprintf("Get Class:%s \n", strClassName);

			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);
	}
}


BOOL CPlistObject::ConvertPlistObject(Json::Value &jsonObject, BOOL bConvertDataPlist/* = FALSE*/)
{
	m_bConvertDataPlist = bConvertDataPlist;

	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 TRUE;
			}
		}
	}

	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
	{
		return FALSE;
	}

	return TRUE;
}


BOOL CPlistObject::ConvertPlistObject(BOOL bConvertDataPlist/* = FALSE*/)
{
	m_bConvertDataPlist = bConvertDataPlist;

	return ConvertPlistObject(m_jsonValues);
}


std::string CPlistObject::ConvertPlistToXML(plist_t plist)
{
	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;
		free(pszMsg);
	}

	return strRet;
}


BOOL CPlistObject::OpenPlist(LPCTSTR lpFilePath)
{
	char *szBuffer = NULL;
	DWORD dwBufLen = 0;
	CFile mFile;
	if (mFile.Open(lpFilePath, CFile::modeRead))
	{
		dwBufLen = (DWORD)mFile.GetLength();
		if (dwBufLen > 8 && dwBufLen < 50 * 1024 * 1024)
		{
			szBuffer = new char[dwBufLen + 1];
			ZeroMemory(szBuffer, dwBufLen + 1);
			mFile.Read(szBuffer, dwBufLen);
		}
		mFile.Close();
	}
	//读文件失败了
	if (!szBuffer)
		return FALSE;

	
	BOOL bRet = OpenPlist(szBuffer, dwBufLen);
	delete[] szBuffer;

	return bRet;
}


BOOL CPlistObject::OpenPlist(LPCSTR lpBuffer, int nBufLen)
{
	if (memcmp(lpBuffer, "bplist00", 8) == 0)
		plist_from_bin(lpBuffer, nBufLen, &m_plistMain);
	else //if (memcmp(lpBuffer, "<?xml", 5) == 0)
		plist_from_xml(lpBuffer, nBufLen, &m_plistMain);

	if (!m_plistMain)
		return FALSE;

	return TRUE;
}


BPlist.h

#pragma once


#include "plist/plist++.h"

class CPlistObject
{
public:
	CPlistObject();
	~CPlistObject();
	void Close();
	plist_t GetNodeByUidDict(plist_t pDict);
	CStringA GetDictClassName(plist_t pDict);

	void SetValue(plist_t pValue, Json::Value &jsonValue);
	BOOL CheckItemType(plist_t pNode, plist_type nType);
	void EnumDictNSMutableArray(plist_t pDictObject, Json::Value &jsonValue);
	void EnumDictNSMutableDictionary(plist_t pDictObject, Json::Value &jsonValue);
	void EnumArray(plist_t pArrObject, Json::Value &jsonValue);
	void EnumDictKeyValueDefault(plist_t pItem, Json::Value &jsonValue);
	void ConvertDict(plist_t pDict, Json::Value &jsonValue);
	BOOL ConvertPlistObject(BOOL bConvertDataPlist = FALSE);
	BOOL ConvertPlistObject(Json::Value &jsonObject, BOOL bConvertDataPlist = FALSE);
	std::string ConvertPlistToXML(plist_t plist = NULL);
	BOOL OpenPlist(LPCTSTR lpFilePath);


	BOOL OpenPlist(LPCSTR lpBuffer, int nBufLen);
	Json::Value m_jsonValues;

	plist_t m_plistMain;
	plist_t m_pArray;
	BOOL m_bConvertDataPlist;
};



评论列表:

发表评论: