324 lines
11 KiB
C#
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();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|