테스트 중
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace UVC.Data.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// DataObject 객체 컬렉션의 변경사항을 추적하는 데이터 배열 클래스
|
||||
/// </summary>
|
||||
public class DataArray : List<DataObject>, IDataObject
|
||||
public partial class DataArray : List<DataObject>, IDataObject
|
||||
{
|
||||
private bool isInPool = false;
|
||||
/// <summary>
|
||||
@@ -164,6 +167,77 @@ namespace UVC.Data.Core
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UniTask 기반 비차단(코루틴 대체) 방식으로 JArray로부터 데이터를 채웁니다. WebGL 같은 싱글스레드 환경에서
|
||||
/// UI 블로킹을 피하기 위해 일정 수의 항목마다 await로 제어권을 양보합니다.
|
||||
/// await myDataArray.FromJArrayIncrementalAsync(jArray, 100, cancellationToken);
|
||||
/// </summary>
|
||||
/// <param name="jArray">JArray 소스</param>
|
||||
/// <param name="batchSize">한 번에 처리할 항목 수</param>
|
||||
/// <param name="cancellationToken">취소 토큰</param>
|
||||
public async UniTask FromJArrayIncrementalAsync(JArray jArray, int batchSize = 50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (jArray == null) return;
|
||||
int count = 0;
|
||||
foreach (var item in jArray)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
base.Add(ConvertToDataObject(item));
|
||||
count++;
|
||||
if (count % batchSize == 0)
|
||||
{
|
||||
await UniTask.Yield(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UniTask 기반 비차단 방식으로 JSON 문자열을 스트리밍하여 파싱합니다.
|
||||
/// JsonTextReader를 사용해 토큰 단위로 읽고, 일정 수의 항목마다 await로 제어권을 양보합니다.
|
||||
/// await myDataArray.FromJsonStringIncrementalAsync(jArray, 100, cancellationToken);
|
||||
/// </summary>
|
||||
/// <param name="jsonString">JSON 문자열</param>
|
||||
/// <param name="batchSize">한 번에 처리할 항목 수</param>
|
||||
/// <param name="cancellationToken">취소 토큰</param>
|
||||
public async UniTask FromJsonStringIncrementalAsync(string jsonString, int batchSize = 50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonString)) return;
|
||||
|
||||
using (var sr = new System.IO.StringReader(jsonString))
|
||||
using (var reader = new Newtonsoft.Json.JsonTextReader(sr))
|
||||
{
|
||||
reader.SupportMultipleContent = false;
|
||||
|
||||
// 기대: StartArray
|
||||
if (!reader.Read() || reader.TokenType != Newtonsoft.Json.JsonToken.StartArray) return;
|
||||
|
||||
int count = 0;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (reader.TokenType == Newtonsoft.Json.JsonToken.StartObject || reader.TokenType == Newtonsoft.Json.JsonToken.StartArray ||
|
||||
reader.TokenType == Newtonsoft.Json.JsonToken.String || reader.TokenType == Newtonsoft.Json.JsonToken.Integer ||
|
||||
reader.TokenType == Newtonsoft.Json.JsonToken.Float || reader.TokenType == Newtonsoft.Json.JsonToken.Boolean)
|
||||
{
|
||||
// JToken.ReadFrom는 현재 토큰에서 전체 값(객체나 배열 등)을 읽습니다.
|
||||
JToken token = JToken.ReadFrom(reader);
|
||||
base.Add(ConvertToDataObject(token));
|
||||
count++;
|
||||
if (count % batchSize == 0)
|
||||
{
|
||||
await UniTask.Yield(cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (reader.TokenType == Newtonsoft.Json.JsonToken.EndArray)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DataArray FromCapacity(int capacity)
|
||||
{
|
||||
Capacity = capacity;
|
||||
@@ -333,7 +407,7 @@ namespace UVC.Data.Core
|
||||
|
||||
/// <summary>
|
||||
/// 현재 데이터 객체의 업데이트된 버전을 생성하고 반환합니다. 선택적으로 메모리 할당 최적화를 위해 풀을 사용합니다.
|
||||
// </summary>
|
||||
/// </summary>
|
||||
/// <remarks>이 메서드는 현재 데이터 객체와 관련 요소의 깊은 복사를 수행합니다.
|
||||
/// <paramref name="fromPool"/>이 <see langword="true"/>이면 메서드는 메모리 사용을 최적화하기 위해 객체 풀에서 새 인스턴스를 검색합니다. 그렇지 않으면 새 인스턴스가 직접 생성됩니다. 반환된
|
||||
/// 객체는 추가, 제거 및 수정을 포함하여 현재 객체의 상태에 대한 업데이트를 반영합니다.
|
||||
@@ -377,7 +451,7 @@ namespace UVC.Data.Core
|
||||
/// <summary>
|
||||
/// 업데이트 된 속성의 수.
|
||||
/// </summary>
|
||||
/// <returns>업데이트된 속성의 총 개수입니다. 업데이트된 속성이 없으면 0을 반환합니다.</returns>
|
||||
/// <returns>업데이트된 속성의 총 개수입니다. 업데이트된 속성이 없으면0을 반환합니다.</returns>
|
||||
public int UpdatedCount { get => addedList.Count + modifiedList.Count + removedList.Count; }
|
||||
|
||||
private bool isUpdateImmediately = false;
|
||||
@@ -471,12 +545,11 @@ namespace UVC.Data.Core
|
||||
/// 현재 <see cref="DataArray"/> 인스턴스의 요소 및 관련 상태를 포함한 깊은 복사본을 생성합니다.
|
||||
///
|
||||
/// </summary>
|
||||
/// <remarks>이 메서드는 현재 배열에 있는 모든 <see cref="DataObject"/> 요소의 깊은 복사본을 포함하는 새로운 <see cref="DataArray"/> 인스턴스를 반환합니다.
|
||||
/// <remarks>이 메서드는 현재 배열에 있는 모든 <see cref="DataObject"/> 요소의 깊은 복사본을 포함하는 새로운 <see cref=\"DataArray\"/> 인스턴스를 반환합니다.
|
||||
/// 복사된 인스턴스는 내부 목록에서 추적하는 추가, 제거 또는 수정된 요소를 포함하여 원본 인스턴스의 상태도 복제합니다.
|
||||
/// </remarks>
|
||||
/// <param name="fromPool">객체 풀에서 복제할지 여부를 지정합니다. 기본값은 true입니다.</param>
|
||||
/// <returns>현재 인스턴스의 깊은 복사본인 새로운 <see cref="DataArray"/> 인스턴스를 반환합니다.</returns>
|
||||
|
||||
public DataArray Copy(bool fromPool = true)
|
||||
{
|
||||
// 풀에서 새 DataArray 인스턴스를 가져옵니다.
|
||||
@@ -540,5 +613,96 @@ namespace UVC.Data.Core
|
||||
dataArray.FromJArray(jArray);
|
||||
return dataArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JToken을 DataObject로 비동기 재질화하여 반환합니다. 이름: ConvertTokenToDataObjectAsync
|
||||
/// </summary>
|
||||
public async UniTask<DataObject> ConvertTokenToDataObjectAsync(JToken token, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (token.Type == JTokenType.Object)
|
||||
{
|
||||
var dataObject = DataObjectPool.Get();
|
||||
await dataObject.FromJObjectIncrementalAsync((JObject)token, batchSize, cancellationToken);
|
||||
return dataObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
var dataObject = DataObjectPool.Get();
|
||||
var value = await MaterializePrimitiveOrCollectionAsync(token, batchSize, cancellationToken);
|
||||
dataObject.Add("value", value);
|
||||
return dataObject;
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask<object?> MaterializePrimitiveOrCollectionAsync(JToken token, int batchSize, CancellationToken cancellationToken)
|
||||
{
|
||||
switch (token.Type)
|
||||
{
|
||||
case JTokenType.String:
|
||||
return token.ToString();
|
||||
case JTokenType.Integer:
|
||||
return token.ToObject<int>();
|
||||
case JTokenType.Float:
|
||||
return token.ToObject<float>();
|
||||
case JTokenType.Boolean:
|
||||
return token.ToObject<bool>();
|
||||
case JTokenType.Array:
|
||||
JArray array = (JArray)token;
|
||||
var dataArray = DataArrayPool.Get();
|
||||
await dataArray.FromJArrayIncrementalAsync(array, batchSize, cancellationToken);
|
||||
return dataArray;
|
||||
case JTokenType.Object:
|
||||
var dataObject = DataObjectPool.Get();
|
||||
await dataObject.FromJObjectIncrementalAsync((JObject)token, batchSize, cancellationToken);
|
||||
return dataObject;
|
||||
default:
|
||||
return token.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// Note: existing synchronous ConvertToDataObject remains for compatibility
|
||||
|
||||
/// <summary>
|
||||
/// 비동기 팩토리: JArray로부터 완전히 점진 재질화된 DataArray를 생성합니다.
|
||||
/// </summary>
|
||||
public static async UniTask<DataArray> CreateFromJArrayAsync(JArray jArray, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var arr = new DataArray();
|
||||
await arr.FromJArrayIncrementalAsync(jArray, batchSize, cancellationToken);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비동기 팩토리: JSON 문자열로부터 완전히 점진 재질화된 DataArray를 생성합니다.
|
||||
/// </summary>
|
||||
public static async UniTask<DataArray> CreateFromJsonStringAsync(string jsonString, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonString)) return new DataArray();
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
var arr = new DataArray();
|
||||
await arr.FromJsonStringIncrementalAsync(jsonString, batchSize, cancellationToken);
|
||||
return arr;
|
||||
#else
|
||||
var jArr = await UniTask.RunOnThreadPool(() => JArray.Parse(jsonString));
|
||||
return await CreateFromJArrayAsync(jArr, batchSize, cancellationToken);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비동기 팩토리: 스트림으로부터 완전히 점진 재질화된 DataArray를 생성합니다.
|
||||
/// </summary>
|
||||
public static async UniTask<DataArray> CreateFromStreamAsync(System.IO.Stream jsonStream, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (jsonStream == null) throw new ArgumentNullException(nameof(jsonStream));
|
||||
using (var sr = new System.IO.StreamReader(jsonStream))
|
||||
using (var reader = new Newtonsoft.Json.JsonTextReader(sr))
|
||||
{
|
||||
reader.SupportMultipleContent = true;
|
||||
var serializer = new Newtonsoft.Json.JsonSerializer();
|
||||
var jArr = serializer.Deserialize<JArray>(reader);
|
||||
if (jArr == null) return new DataArray();
|
||||
return await CreateFromJArrayAsync(jArr, batchSize, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UVC.Extention;
|
||||
|
||||
namespace UVC.Data.Core
|
||||
@@ -26,8 +29,8 @@ namespace UVC.Data.Core
|
||||
/// // 필드 이름 변환 설정
|
||||
/// mask.NamesForReplace = new Dictionary<string, string>
|
||||
/// {
|
||||
/// { "userName", "name" },
|
||||
/// { "userEmail", "email" }
|
||||
/// { "userName", "name" },
|
||||
/// { "userEmail", "email" }
|
||||
/// };
|
||||
///
|
||||
/// // JObject 속성으로 마스킹 규칙 추가
|
||||
@@ -46,7 +49,7 @@ namespace UVC.Data.Core
|
||||
/// // - password는 제외됨
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class DataObject : OrderedDictionary<string, object?>, IDataObject
|
||||
public partial class DataObject : OrderedDictionary<string, object?>, IDataObject
|
||||
{
|
||||
private bool isInPool = false;
|
||||
/// <summary>
|
||||
@@ -118,7 +121,7 @@ namespace UVC.Data.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id에 해당하는 key 문자열
|
||||
/// Id에 해당하는 key 문자열
|
||||
/// </summary>
|
||||
public string? IdKey { get; set; } = null;
|
||||
|
||||
@@ -286,11 +289,142 @@ namespace UVC.Data.Core
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// UniTask 기반 비차단 방식으로 JObject로부터 데이터를 채웁니다. WebGL 같은 싱글스레드 환경에서
|
||||
/// UI 블로킹을 피하기 위해 일정 수의 키마다 await로 제어권을 양보합니다.
|
||||
/// await myDataObject.FromJObjectIncrementalAsync(jObject, 200, cancellationToken);
|
||||
/// </summary>
|
||||
/// <param name="other">JObject 소스</param>
|
||||
/// <param name="batchSize">한 번에 처리할 속성 수</param>
|
||||
/// <param name="cancellationToken">취소 토큰</param>
|
||||
public async UniTask FromJObjectIncrementalAsync(JObject other, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (other == null) return;
|
||||
IdKey = null;
|
||||
Name = string.Empty;
|
||||
_isInitializing = true;
|
||||
int count =0;
|
||||
try
|
||||
{
|
||||
foreach (var prop in other.Properties())
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
var value = await ConvertJTokenToObjectAsync(prop.Value, batchSize, cancellationToken);
|
||||
this[prop.Name] = value;
|
||||
#else
|
||||
this[prop.Name] = ConvertJTokenToObject(prop.Value);
|
||||
#endif
|
||||
count++;
|
||||
if (count % batchSize ==0)
|
||||
{
|
||||
await UniTask.Yield(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isInitializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UniTask 기반 비차단 방식으로 JSON 문자열을 스트리밍하여 파싱합니다.
|
||||
/// JsonTextReader를 사용해 토큰 단위로 읽고, 일정 수의 항목마다 await로 제어권을 양보합니다.
|
||||
/// await myDataObject.FromJsonStringIncrementalAsync(jsonString, 200, cancellationToken);
|
||||
/// </summary>
|
||||
/// <param name="jsonString">JSON 문자열</param>
|
||||
/// <param name="batchSize">한 번에 처리할 속성 수</param>
|
||||
/// <param name="cancellationToken">취소 토큰</param>
|
||||
public async UniTask FromJsonStringIncrementalAsync(string jsonString, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonString)) return;
|
||||
|
||||
using (var sr = new System.IO.StringReader(jsonString))
|
||||
using (var reader = new Newtonsoft.Json.JsonTextReader(sr))
|
||||
{
|
||||
reader.SupportMultipleContent = false;
|
||||
|
||||
// 기대: StartObject
|
||||
if (!reader.Read() || reader.TokenType != Newtonsoft.Json.JsonToken.StartObject) return;
|
||||
|
||||
IdKey = null;
|
||||
Name = string.Empty;
|
||||
_isInitializing = true;
|
||||
int count =0;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (reader.TokenType == Newtonsoft.Json.JsonToken.PropertyName)
|
||||
{
|
||||
string propName = (string)reader.Value!;
|
||||
|
||||
// 다음 토큰은 값의 시작이어야 함. JToken.ReadFrom를 사용하여 값 전체를 읽는다.
|
||||
if (!reader.Read()) break;
|
||||
|
||||
JToken token = JToken.ReadFrom(reader);
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
var value = await ConvertJTokenToObjectAsync(token, batchSize, cancellationToken);
|
||||
this[propName] = value;
|
||||
#else
|
||||
this[propName] = ConvertJTokenToObject(token);
|
||||
#endif
|
||||
|
||||
count++;
|
||||
if (count % batchSize ==0)
|
||||
{
|
||||
await UniTask.Yield(cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (reader.TokenType == Newtonsoft.Json.JsonToken.EndObject)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_isInitializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JToken을 적절한 C# 객체 타입으로 변환하는 헬퍼 메서드입니다.
|
||||
/// </summary>
|
||||
/// <param name="token">변환할 JToken 객체</param>
|
||||
/// <returns>변환된 C# 객체</returns>
|
||||
private async UniTask<object?> MaterializeJTokenAsync(JToken token, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
switch (token.Type)
|
||||
{
|
||||
case JTokenType.String:
|
||||
return token.ToString();
|
||||
case JTokenType.Integer:
|
||||
return token.ToObject<int>();
|
||||
case JTokenType.Float:
|
||||
return token.ToObject<float>();
|
||||
case JTokenType.Boolean:
|
||||
return token.ToObject<bool>();
|
||||
case JTokenType.Object:
|
||||
// 점진적으로 DataObject로 채우기
|
||||
var dataObject = DataObjectPool.Get();
|
||||
await dataObject.FromJObjectIncrementalAsync((JObject)token, batchSize, cancellationToken);
|
||||
return dataObject;
|
||||
case JTokenType.Array:
|
||||
JArray array = (JArray)token;
|
||||
var dataArray = DataArrayPool.Get();
|
||||
await dataArray.FromJArrayIncrementalAsync(array, batchSize, cancellationToken);
|
||||
return dataArray;
|
||||
default:
|
||||
return token.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 기존 ConvertJTokenToObject는 동기 API를 유지하되, 호출자가 비동기 전환을 원하면
|
||||
/// MaterializeJTokenAsync를 직접 호출하도록 합니다. 기본 동작은 기존과 동일합니다.
|
||||
/// </summary>
|
||||
private object? ConvertJTokenToObject(JToken token)
|
||||
{
|
||||
switch (token.Type)
|
||||
@@ -315,6 +449,15 @@ namespace UVC.Data.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새 비동기 전환용 헬퍼: 동기 ConvertJTokenToObject 대신 사용하여 비동기적으로 토큰을 재질화합니다.
|
||||
/// 메서드 이름: ConvertJTokenToObjectAsync
|
||||
/// </summary>
|
||||
public UniTask<object?> ConvertJTokenToObjectAsync(JToken token, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return MaterializeJTokenAsync(token, batchSize, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 프로퍼티를 변경된 것으로 표시합니다.
|
||||
/// 전체 데이터가 갱신되었을 때 사용합니다.
|
||||
@@ -383,14 +526,13 @@ namespace UVC.Data.Core
|
||||
/// <returns>변환된 정수 값 또는 기본값</returns>
|
||||
public int? GetInt(string propertyName, int? defaultValue = null)
|
||||
{
|
||||
if (!TryGetValue(propertyName, out var v) || v == null) return defaultValue;
|
||||
return v switch
|
||||
if (TryGetValue(propertyName, out object? value) && value != null)
|
||||
{
|
||||
int i => i,
|
||||
long l when l >= int.MinValue && l <= int.MaxValue => (int)l,
|
||||
string s when int.TryParse(s, out var i2) => i2,
|
||||
_ => defaultValue
|
||||
};
|
||||
if (value is int intValue)
|
||||
return intValue;
|
||||
return Convert.ToInt32(value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -573,7 +715,7 @@ namespace UVC.Data.Core
|
||||
return null; // 깊이 제한 초과 시 탐색 중단
|
||||
}
|
||||
|
||||
// 1. 현재 객체에서 직접 속성 검색
|
||||
//1. 현재 객체에서 직접 속성 검색
|
||||
if (TryGetValue(propertyName, out object? value) && value != null)
|
||||
{
|
||||
if (value is DataObject dataObject)
|
||||
@@ -592,12 +734,12 @@ namespace UVC.Data.Core
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 중첩된 DataObject에서 재귀적으로 검색
|
||||
//2. 중첩된 DataObject에서 재귀적으로 검색
|
||||
foreach (KeyValuePair<string, object?> keyValue in this)
|
||||
{
|
||||
if (keyValue.Value is DataObject nestedDataObject)
|
||||
{
|
||||
// 재귀 호출 시 깊이를 1 증가
|
||||
// 재귀 호출 시 깊이를1 증가
|
||||
DataObject? foundInNested = nestedDataObject.GetDataObjectInternal(propertyName, currentDepth + 1, maxDepth);
|
||||
if (foundInNested != null)
|
||||
{
|
||||
@@ -692,13 +834,7 @@ namespace UVC.Data.Core
|
||||
/// </summary>
|
||||
public void RemoveAll()
|
||||
{
|
||||
if (Count == 0)
|
||||
{
|
||||
changedProperies.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var value in Values)
|
||||
foreach (var value in Values.ToList())
|
||||
{
|
||||
if (value is DataObject dataObject)
|
||||
{
|
||||
@@ -823,8 +959,8 @@ namespace UVC.Data.Core
|
||||
foreach (var keyValue in otherDataObject)
|
||||
{
|
||||
if (!this.ContainsKey(keyValue.Key) || (this[keyValue.Key] == null && keyValue.Value != null)
|
||||
|| (this[keyValue.Key] != null && keyValue.Value == null)
|
||||
|| (this[keyValue.Key] != null && keyValue.Value != null && !this[keyValue.Key]!.Equals(keyValue.Value)))
|
||||
|| (this[keyValue.Key] != null && keyValue.Value == null)
|
||||
|| (this[keyValue.Key] != null && keyValue.Value != null && !this[keyValue.Key]!.Equals(keyValue.Value)))
|
||||
{
|
||||
//참조 타입과 값 타입 구분하여 복사
|
||||
object? valueToSet;
|
||||
@@ -866,7 +1002,7 @@ namespace UVC.Data.Core
|
||||
/// cref="DataArray"/> 유형의 속성은 깊이 복사되고, 다른 유형의 속성은 직접 복사되도록 합니다. <c>IdKey</c>가 없으면
|
||||
/// 현재 객체의 첫 번째 키-값 쌍이 업데이트된 객체에 포함됩니다.
|
||||
///</remarks>
|
||||
/// <param name="fromPool">업데이트된 객체를 풀에서 검색해야 하는지 여부를 나타내는 값입니다. <see langword="true"/>인 경우
|
||||
/// <param name="fromPool">업데이트된 객체가 풀에서 검색해야 하는지 여부를 나타내는 값입니다. <see langword="true"/>인 경우
|
||||
/// <see cref="DataObjectPool"/>에서 객체를 가져옵니다. 그렇지 않으면 새 <see cref="DataObject"/>
|
||||
/// 인스턴스가 생성됩니다.</param>
|
||||
/// <returns>현재 객체에서 복사된 속성과 값을 포함하는 업데이트된 <see cref="IDataObject"/> 인스턴스입니다.
|
||||
@@ -912,7 +1048,6 @@ namespace UVC.Data.Core
|
||||
|
||||
public void ReturnToPool()
|
||||
{
|
||||
if (IsInPool) return; // 중복 반환 방지
|
||||
if (CreatedFromPool)
|
||||
{
|
||||
Reset();
|
||||
@@ -926,7 +1061,7 @@ namespace UVC.Data.Core
|
||||
/// <summary>
|
||||
/// 업데이트 된 속성의 수.
|
||||
/// </summary>
|
||||
/// <returns>업데이트된 속성의 총 개수입니다. 업데이트된 속성이 없으면 0을 반환합니다.</returns>
|
||||
/// <returns>업데이트된 속성의 총 개수입니다. 업데이트된 속성이 없으면0을 반환합니다.</returns>
|
||||
public int UpdatedCount { get => changedProperies.Count; }
|
||||
|
||||
private bool isUpdateImmediately = false;
|
||||
@@ -975,6 +1110,49 @@ namespace UVC.Data.Core
|
||||
return ToJObject().ToString(Newtonsoft.Json.Formatting.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비동기 팩토리: JObject로부터 완전히 점진 재질화된 DataObject를 생성합니다.
|
||||
/// </summary>
|
||||
public static async UniTask<DataObject> CreateFromJObjectAsync(JObject other, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var obj = new DataObject();
|
||||
await obj.FromJObjectIncrementalAsync(other, batchSize, cancellationToken);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비동기 팩토리: JSON 문자열로부터 완전히 점진 재질화된 DataObject를 생성합니다.
|
||||
/// </summary>
|
||||
public static async UniTask<DataObject> CreateFromJsonStringAsync(string jsonString, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonString)) return new DataObject();
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
var obj = new DataObject();
|
||||
await obj.FromJsonStringIncrementalAsync(jsonString, batchSize, cancellationToken);
|
||||
return obj;
|
||||
// WebGL: 스레드가 없으므로 incremental 파서를 사용해 메인스레드에서 점진 처리
|
||||
#else
|
||||
// non-WebGL: 스레드풀에서 전체 파싱 후 비동기 재질화
|
||||
var jObj = await UniTask.RunOnThreadPool(() => JObject.Parse(jsonString));
|
||||
return await CreateFromJObjectAsync(jObj, batchSize, cancellationToken);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비동기 팩토리: 스트림으로부터 완전히 점진 재질화된 DataObject를 생성합니다.
|
||||
/// </summary>
|
||||
public static async UniTask<DataObject> CreateFromStreamAsync(System.IO.Stream jsonStream, int batchSize =50, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (jsonStream == null) throw new ArgumentNullException(nameof(jsonStream));
|
||||
using (var sr = new System.IO.StreamReader(jsonStream))
|
||||
using (var reader = new Newtonsoft.Json.JsonTextReader(sr))
|
||||
{
|
||||
reader.SupportMultipleContent = true;
|
||||
var serializer = new Newtonsoft.Json.JsonSerializer();
|
||||
var jObj = serializer.Deserialize<JObject>(reader);
|
||||
if (jObj == null) return new DataObject();
|
||||
return await CreateFromJObjectAsync(jObj, batchSize, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Data.Http;
|
||||
using UVC.Log;
|
||||
using UVC.network;
|
||||
using UVC.Tests;
|
||||
|
||||
namespace UVC.Data.Mqtt
|
||||
@@ -197,7 +198,7 @@ namespace UVC.Data.Mqtt
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
if (UseMockup)
|
||||
{
|
||||
ULog.Warn("WebGL Mockup 모드는 아직 구현되지 않았습니다.");
|
||||
Debug.LogWarning("WebGL Mockup 모드는 아직 구현되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
if (webGlMqttService != null) return;
|
||||
|
||||
@@ -169,8 +169,9 @@ namespace UVC.Data
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static UserSetting? FromDataObject(DataObject dataObject)
|
||||
{
|
||||
if (dataObject == null) return null;
|
||||
|
||||
Reference in New Issue
Block a user