近期發現無論是系統的System.Xml還是Mono.Xml,其實都有這樣或者那樣的問題,所以決定自己搞一個快一點的xml parse.以前在C++里用過rapidxml,這個確實是神一般的存在,速度那是相當快,所以設想能否直接包裝一下,然后讓它可以在unity中使用呢?
實測了一下,確定是可行的,所以把代碼放到了github上,github上的代碼應該一直都是比較新的:https://github.com/sczybt/UnityRapidXml
可以看到在parse上,有至少10倍的速度優勢.這些測試性能的代碼可以在XmlPerformanceTests中找到.
下面是簡單訪問數據的性能對比:
可以看到差距並不大,由於存在着托管與native的轉換,所以UnityRapidXml並沒有優勢.但是一旦發生復雜搜索的時候,還是有優勢的,另外UnityRapidXml內置了獲取屬性值針對各個類型的接口,這樣字符串到bool/int/float等數據的轉換在native層完成,這樣效率更高.
下面直接放目前最新的代碼,以后代碼更新不更新下面的代碼,需要最新的代碼建議去github上面取.
RapidXml.cs
//
using System;
using System.Collections.Generic; using System.Runtime.InteropServices; using System.Diagnostics; namespace RapidXml { // Attribute public struct NodeAttribute { public RapidXmlParser Document; public IntPtr NativeAttrPtr; [Conditional("UNITY_EDITOR")] public static void EditorAssert(bool bInCondition) { if (!bInCondition) { UnityEngine.Debug.DebugBreak(); } } public bool IsValid() { return Document != null && NativeAttrPtr != IntPtr.Zero; } public string GetName() { EditorAssert(IsValid()); IntPtr Result = RapidXmlParser.GetAttributeNamePtr(Document.NativeDocumentPtr, NativeAttrPtr); return Result != IntPtr.Zero ? Marshal.PtrToStringAnsi(Result) : ""; } public string GetValue() { EditorAssert(IsValid()); IntPtr Result = RapidXmlParser.GetAttributeValuePtr(Document.NativeDocumentPtr, NativeAttrPtr); return Result != IntPtr.Zero ? Marshal.PtrToStringAnsi(Result) : ""; } public bool GetBool() { EditorAssert(IsValid()); return RapidXmlParser.GetAttributeValueBool(Document.NativeDocumentPtr, NativeAttrPtr); } public int GetInt() { EditorAssert(IsValid()); return RapidXmlParser.GetAttributeValueInt(Document.NativeDocumentPtr, NativeAttrPtr); } public uint GetUInt() { EditorAssert(IsValid()); return RapidXmlParser.GetAttributeValueUInt(Document.NativeDocumentPtr, NativeAttrPtr); } public Int64 GetInt64() { EditorAssert(IsValid()); return RapidXmlParser.GetAttributeValueInt64(Document.NativeDocumentPtr, NativeAttrPtr); } public UInt64 GetUInt64() { EditorAssert(IsValid()); return RapidXmlParser.GetAttributeValueUInt(Document.NativeDocumentPtr, NativeAttrPtr); } public float GetFloat() { EditorAssert(IsValid()); return RapidXmlParser.GetAttributeValueFloat(Document.NativeDocumentPtr, NativeAttrPtr); } public double GetDouble() { EditorAssert(IsValid()); return RapidXmlParser.GetAttributeValueDouble(Document.NativeDocumentPtr, NativeAttrPtr); } public NodeAttribute NextAttribute(string InName = null) { EditorAssert(IsValid()); NodeAttribute Attr = new NodeAttribute(); Attr.Document = this.Document; Attr.NativeAttrPtr = string.IsNullOrEmpty(InName) ? RapidXmlParser.NextAttributePtr(Document.NativeDocumentPtr, NativeAttrPtr) : RapidXmlParser.NextAttributePtrWithName(Document.NativeDocumentPtr, NativeAttrPtr, InName); return Attr; } } public struct NodeElement { public RapidXmlParser Document; public IntPtr NativeNodePtr; [Conditional("UNITY_EDITOR")] public static void EditorAssert(bool bInCondition) { if (!bInCondition) { UnityEngine.Debug.DebugBreak(); } } public bool IsValid() { return Document != null && NativeNodePtr != IntPtr.Zero; } public NodeElement FirstNode(string InName = null) { EditorAssert(IsValid()); NodeElement Element = new NodeElement(); Element.Document = Document; Element.NativeNodePtr = string.IsNullOrEmpty(InName) ? RapidXmlParser.FirstNodePtr(Document.NativeDocumentPtr, NativeNodePtr) : RapidXmlParser.FirstNodePtrWithName(Document.NativeDocumentPtr, NativeNodePtr, InName); return Element; } public NodeElement NextSibling(string InName = null) { EditorAssert(IsValid()); NodeElement Element = new NodeElement(); Element.Document = Document; Element.NativeNodePtr = string.IsNullOrEmpty(InName) ? RapidXmlParser.NextSiblingPtr(Document.NativeDocumentPtr, NativeNodePtr) : RapidXmlParser.NextSiblingPtrWithName(Document.NativeDocumentPtr, NativeNodePtr, InName); return Element; } public NodeAttribute FirstAttribute(string InName= null) { EditorAssert(IsValid()); NodeAttribute Attr = new NodeAttribute(); Attr.Document = this.Document; Attr.NativeAttrPtr = string.IsNullOrEmpty(InName) ? RapidXmlParser.FirstAttributePtr(Document.NativeDocumentPtr, NativeNodePtr) : RapidXmlParser.FirstAttributePtrWithName(Document.NativeDocumentPtr, NativeNodePtr, InName); return Attr; } public bool HasAttribute(String InName) { EditorAssert(IsValid()); return RapidXmlParser.HasAttribute(Document.NativeDocumentPtr, NativeNodePtr, InName); } public bool AttributeBool(String InName) { EditorAssert(IsValid()); return RapidXmlParser.AttributeBool(Document.NativeDocumentPtr, NativeNodePtr, InName); } public int AttributeInt(string InName) { EditorAssert(IsValid()); return RapidXmlParser.AttributeInt(Document.NativeDocumentPtr, NativeNodePtr, InName); } public uint AttributeUInt(string InName) { EditorAssert(IsValid()); return RapidXmlParser.AttributeUInt(Document.NativeDocumentPtr, NativeNodePtr, InName); } public float AttributeFloat(String InName) { EditorAssert(IsValid()); return RapidXmlParser.AttributeFloat(Document.NativeDocumentPtr, NativeNodePtr, InName); } public string AttributeString(string InName) { EditorAssert(IsValid()); IntPtr Result = RapidXmlParser.AttributeStringPtr(Document.NativeDocumentPtr, NativeNodePtr, InName); return Result != IntPtr.Zero ? Marshal.PtrToStringAnsi(Result) : ""; } // the same with AttributeString // created for compatible public string Attribute(string InName) { EditorAssert(IsValid()); IntPtr Result = RapidXmlParser.AttributeStringPtr(Document.NativeDocumentPtr, NativeNodePtr, InName); return Result != IntPtr.Zero ? Marshal.PtrToStringAnsi(Result) : ""; } public string GetName() { EditorAssert(IsValid()); IntPtr Result = RapidXmlParser.GetNodeTagPtr(Document.NativeDocumentPtr, NativeNodePtr); return Result != IntPtr.Zero ? Marshal.PtrToStringAnsi(Result) : ""; } public int GetChildNodeCount() { EditorAssert(IsValid()); return RapidXmlParser.GetChildNodeCount(Document.NativeDocumentPtr, NativeNodePtr); } public int GetAttributeCount() { EditorAssert(IsValid()); return RapidXmlParser.GetAttributeCount(Document.NativeDocumentPtr, NativeNodePtr); } } public class RapidXmlParser : IDisposable { public const string PluginName = "RapidXml"; public IntPtr NativeDocumentPtr = IntPtr.Zero; public void Load(string InContent) { NativeDocumentPtr = LoadFromString(InContent); string ErrorMessage = Marshal.PtrToStringAnsi(GetLastErrorMessage(NativeDocumentPtr)); if (!string.IsNullOrEmpty(ErrorMessage)) { throw new Exception(ErrorMessage); } } public void Dispose() { if (NativeDocumentPtr != IntPtr.Zero) { DisposeThis(NativeDocumentPtr); NativeDocumentPtr = IntPtr.Zero; } } public NodeElement FirstNode(string InName = null) { NodeElement Element = new NodeElement(); Element.Document = this; Element.NativeNodePtr = string.IsNullOrEmpty(InName) ? FirstNodePtr(NativeDocumentPtr, IntPtr.Zero) : FirstNodePtrWithName(NativeDocumentPtr, IntPtr.Zero, InName); return Element; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // internal use ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif private static extern IntPtr LoadFromString([MarshalAs(UnmanagedType.LPStr)]string InContent); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif private static extern IntPtr GetLastErrorMessage(IntPtr InDocumentNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif private static extern void DisposeThis(IntPtr InDocumentNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr FirstAttributePtr(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr FirstAttributePtrWithName(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr NextAttributePtr(IntPtr InDocumentNativePtr, IntPtr InAttrPtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr NextAttributePtrWithName(IntPtr InDocumentNativePtr, IntPtr InAttrPtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern bool HasAttribute(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern bool AttributeBool(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern int AttributeInt(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern uint AttributeUInt(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern Int64 AttributeInt64(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern UInt64 AttributeUInt64(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern float AttributeFloat(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern double AttributeDouble(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr AttributeStringPtr(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr FirstNodePtr(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr FirstNodePtrWithName(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr NextSiblingPtr(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr NextSiblingPtrWithName(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr, [MarshalAs(UnmanagedType.LPStr)]String InName); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr GetNodeTagPtr(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern int GetChildNodeCount(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern int GetAttributeCount(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr GetAttributeNamePtr(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern IntPtr GetAttributeValuePtr(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern bool GetAttributeValueBool(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern int GetAttributeValueInt(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern uint GetAttributeValueUInt(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else [DllImport(PluginName, CallingConvention = CallingConvention.Cdecl)] #endif internal static extern int GetAttributeValueInt64(IntPtr InDocumentNativePtr, IntPtr InNodeNativePtr); #if UNITY_IPHONE && !UNITY_EDITOR [DllImport("__Internal")] #else