Files
EnglewoodLAB/Assets/Scripts/UVC/Util/XmlDataUtil.cs

324 lines
11 KiB
C#

using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using UnityEngine;
using UnityEngine.Networking;
namespace UVC.Util
{
/// <summary>
/// XML 데이터 처리 유틸리티 클래스
/// </summary>
public class XmlDataUtil : MonoBehaviour
{
private static XmlDataUtil __instance = null;
public static XmlDataUtil Instance
{
get
{
if(__instance == null)
{
__instance = new GameObject("_XmlDataUtil").AddComponent<XmlDataUtil>();
}
return __instance;
}
}
private void Awake()
{
DontDestroyOnLoad(gameObject);
}
/// <summary>
/// 데이터 직렬화 + XML 파일 저장
/// </summary>
/// <param name="filePath">저장될 파일 경로</param>
/// <param name="data">직렬화할 데이터</param>
/// <example>
/// <code>
/// var data = new MyData { Name = "Test", Age = 25 };
/// XmlDataUtil.Instance.SerializeXmlData("path/to/file.xml", data);
/// </code>
/// </example>
public void SerializeXmlData(string filePath, object data)
{
XmlSerializer serializer = new XmlSerializer(data.GetType());
using (TextWriter writer = new StreamWriter(filePath))
{
serializer.Serialize(writer, data);
}
}
/// <summary>
/// 데이터 비직렬화 + XML 파일 로드
/// </summary>
/// <typeparam name="T">비직렬화할 데이터 타입</typeparam>
/// <param name="filePath">XML 파일 경로</param>
/// <param name="completeAction">로드 완료 후 실행할 콜백</param>
/// <example>
/// <code>
/// XmlDataUtil.Instance.DeserializeXmlData<MyData>("path/to/file.xml", (data) =>
/// {
/// Debug.Log($"Name: {data.Name}, Age: {data.Age}");
/// });
/// </code>
/// </example>
public void DeserializeXmlData<T>(string filePath, Action<T> completeAction)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
XmlReader reader = XmlReader.Create(fs);
if (completeAction != null)
{
completeAction.Invoke((T)serializer.Deserialize(reader));
}
}
}
/// <summary>
/// XML 데이터 저장
/// </summary>
/// <param name="filePath">저장될 파일 경로</param>
/// <param name="data">저장할 데이터</param>
/// <example>
/// <code>
/// var data = new MyData { Name = "Test", Age = 25 };
/// XmlDataUtil.Instance.SaveXmlData("path/to/file.xml", data);
/// </code>
/// </example>
public void SaveXmlData(string filePath, object data)
{
if (data == null) return;
//파일이 있으면 기존 파일 로드 후 수정
if(File.Exists(filePath))
{
StartCoroutine(LoadXml(filePath, (XDocument xdoc) =>
{
UpdateData(xdoc, data, (isChanged, isDataError) =>
{
if (isChanged && !isDataError)
{
xdoc.Save(filePath);
Debug.Log("SaveXmlData!");
}
else if (isDataError)
{
Debug.Log("XML 데이터 노드 순서가 잘못되었다.! 다시 강제로 직렬화한다.");
SerializeXmlData(filePath, data);
}
});
}));
}
//파일이 없으면 새롭게 직렬화, 저장
else
{
SerializeXmlData(filePath, data);
}
}
/// <summary>
/// XML 데이터 로드
/// </summary>
/// <param name="filePath">XML 파일 경로</param>
/// <param name="OnLoadComplete">로드 완료 이벤트 핸들러</param>
/// <param name="OnError">에러 핸들러</param>
/// <returns>코루틴</returns>
/// <example>
/// <code>
/// StartCoroutine(XmlDataUtil.Instance.LoadXml("path/to/file.xml", (xdoc) =>
/// {
/// Debug.Log(xdoc.ToString());
/// }, (error) =>
/// {
/// Debug.LogError(error);
/// }));
/// </code>
/// </example>
public IEnumerator LoadXml(string filePath, Action<XDocument> OnLoadComplete, Action<string> OnError = null)
{
if (!filePath.Contains("://"))
{
filePath = "file://" + filePath;
}
using (UnityWebRequest www = UnityWebRequest.Get(filePath))
{
www.timeout = 5;//5초
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
if (OnError != null)
{
OnError(www.error);
}
}
else
{
using (MemoryStream memStream = new MemoryStream())
{
memStream.Write(www.downloadHandler.data, 0, www.downloadHandler.data.Length);
memStream.Position = 0;
XmlReader reader = XmlReader.Create(memStream);
XDocument xdoc = XDocument.Load(reader);
if (OnLoadComplete != null)
{
OnLoadComplete(xdoc);
}
}
}
}
}
/// <summary>
/// XML 데이터를 비직렬화하여 로드합니다.
/// </summary>
/// <typeparam name="T">비직렬화할 데이터 타입</typeparam>
/// <param name="filePath">XML 파일 경로</param>
/// <param name="OnLoadComplete">로드 완료 후 실행할 콜백 (비직렬화된 데이터 전달)</param>
/// <param name="OnError">에러 발생 시 실행할 콜백 (에러 메시지 전달)</param>
/// <returns>코루틴</returns>
/// <example>
/// <code>
/// StartCoroutine(XmlDataUtil.Instance.LoadXml<MyData>("path/to/file.xml", (data) =>
/// {
/// Debug.Log($"Name: {data.Name}, Age: {data.Age}");
/// }, (error) =>
/// {
/// Debug.LogError($"Error loading XML: {error}");
/// }));
/// </code>
/// </example>
[Obsolete]
public IEnumerator LoadXml<T>(string filePath, Action<T> OnLoadComplete, Action<string> OnError = null)
{
if (!filePath.Contains("://"))
{
filePath = "file://" + filePath;
}
using (UnityWebRequest www = UnityWebRequest.Get(filePath))
{
www.timeout = 5;//5초
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
if (OnError != null)
{
OnError(www.error);
}
}
else
{
using (MemoryStream memStream = new MemoryStream())
{
memStream.Write(www.downloadHandler.data, 0, www.downloadHandler.data.Length);
memStream.Position = 0;
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlReader reader = XmlReader.Create(memStream);
T result = (T)serializer.Deserialize(reader);
if (OnLoadComplete != null)
{
OnLoadComplete(result);
}
}
}
}
}
private void UpdateData(XDocument xdoc, object data, Action<bool, bool> OnComplete)
{
XmlSerializer xmlSerializer = new XmlSerializer(data.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, data);
var currentData = XElement.Parse(textWriter.ToString());
bool isChanged = false;
bool isDataError = false;
var currentItemList = currentData.Descendants().ToList();
var originItemList = xdoc.Root.Descendants().ToList();
foreach (var currentItem in currentItemList)
{
foreach (var originItem in originItemList)
{
if (currentItemList.IndexOf(currentItem) == originItemList.IndexOf(originItem))
{
if (currentItem.Name.Equals(originItem.Name))
{
if (!currentItem.Value.Equals(originItem.Value) && !originItem.HasElements)
{
originItem.SetValue(currentItem.Value);
isChanged = true;
}
foreach (var currentAtt in currentItem.Attributes())
{
foreach (var originAtt in originItem.Attributes())
{
if (!currentAtt.Value.Equals(originAtt.Value))
{
originAtt.SetValue(currentAtt.Value);
isChanged = true;
}
}
}
}
else
{
isDataError = true;
break;
}
}
}
}
if(OnComplete != null)
{
OnComplete(isChanged, isDataError);
}
}
}
/// <summary>
/// XML 데이터를 문자열로 직렬화
/// </summary>
/// <param name="toSerialize">직렬화할 객체</param>
/// <returns>XML 문자열</returns>
/// <example>
/// <code>
/// var xmlString = XmlDataUtil.ToXmlString(new MyData { Name = "Test", Age = 25 });
/// Debug.Log(xmlString);
/// </code>
/// </example>
public static string ToXmlString(object toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
}
}