近期发现无论是系统的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