No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MultiJsonInternal.cs 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using System.Text;
  5. using UnityEditor.Rendering;
  6. using UnityEngine;
  7. using UnityEngine.Rendering;
  8. using UnityEditor.ShaderGraph.Internal;
  9. using UnityEditor.Graphing;
  10. using UnityEngine.UIElements;
  11. using UnityEditor.ShaderGraph.Drawing;
  12. namespace UnityEditor.ShaderGraph.Serialization
  13. {
  14. static class MultiJsonInternal
  15. {
  16. #region Unknown Data Handling
  17. public class UnknownJsonObject : JsonObject
  18. {
  19. public string typeInfo;
  20. public string jsonData;
  21. public JsonData<JsonObject> castedObject;
  22. public UnknownJsonObject(string typeInfo)
  23. {
  24. this.typeInfo = typeInfo;
  25. }
  26. public override void Deserailize(string typeInfo, string jsonData)
  27. {
  28. this.jsonData = jsonData;
  29. }
  30. public override string Serialize()
  31. {
  32. return jsonData;
  33. }
  34. public override void OnAfterDeserialize(string json)
  35. {
  36. if (castedObject.value != null)
  37. {
  38. Enqueue(castedObject, json.Trim());
  39. }
  40. }
  41. public override void OnAfterMultiDeserialize(string json)
  42. {
  43. if (castedObject.value == null)
  44. {
  45. //Never got casted so nothing ever reffed this object
  46. //likely that some other unknown json object had a ref
  47. //to this thing. Need to include it in the serialization
  48. //step of the object still.
  49. if (jsonBlobs.TryGetValue(currentRoot.objectId, out var blobs))
  50. {
  51. blobs[objectId] = jsonData.Trim();
  52. }
  53. else
  54. {
  55. var lookup = new Dictionary<string, string>();
  56. lookup[objectId] = jsonData.Trim();
  57. jsonBlobs.Add(currentRoot.objectId, lookup);
  58. }
  59. }
  60. }
  61. public override T CastTo<T>()
  62. {
  63. if (castedObject.value != null)
  64. return castedObject.value.CastTo<T>();
  65. Type t = typeof(T);
  66. if (t == typeof(AbstractMaterialNode) || t.IsSubclassOf(typeof(AbstractMaterialNode)))
  67. {
  68. UnknownNodeType unt = new UnknownNodeType(jsonData);
  69. valueMap[objectId] = unt;
  70. s_ObjectIdField.SetValue(unt, objectId);
  71. castedObject = unt;
  72. return unt.CastTo<T>();
  73. }
  74. else if (t == typeof(Target) || t.IsSubclassOf(typeof(Target)))
  75. {
  76. UnknownTargetType utt = new UnknownTargetType(typeInfo, jsonData);
  77. valueMap[objectId] = utt;
  78. s_ObjectIdField.SetValue(utt, objectId);
  79. castedObject = utt;
  80. return utt.CastTo<T>();
  81. }
  82. else if (t == typeof(SubTarget) || t.IsSubclassOf(typeof(SubTarget)))
  83. {
  84. UnknownSubTargetType ustt = new UnknownSubTargetType(typeInfo, jsonData);
  85. valueMap[objectId] = ustt;
  86. s_ObjectIdField.SetValue(ustt, objectId);
  87. castedObject = ustt;
  88. return ustt.CastTo<T>();
  89. }
  90. else if (t == typeof(ShaderInput) || t.IsSubclassOf(typeof(ShaderInput)))
  91. {
  92. UnknownShaderPropertyType usp = new UnknownShaderPropertyType(typeInfo, jsonData);
  93. valueMap[objectId] = usp;
  94. s_ObjectIdField.SetValue(usp, objectId);
  95. castedObject = usp;
  96. return usp.CastTo<T>();
  97. }
  98. else if (t == typeof(MaterialSlot) || t.IsSubclassOf(typeof(MaterialSlot)))
  99. {
  100. UnknownMaterialSlotType umst = new UnknownMaterialSlotType(typeInfo, jsonData);
  101. valueMap[objectId] = umst;
  102. s_ObjectIdField.SetValue(umst, objectId);
  103. castedObject = umst;
  104. return umst.CastTo<T>();
  105. }
  106. else if (t == typeof(AbstractShaderGraphDataExtension) || t.IsSubclassOf(typeof(AbstractShaderGraphDataExtension)))
  107. {
  108. UnknownGraphDataExtension usge = new UnknownGraphDataExtension(typeInfo, jsonData);
  109. valueMap[objectId] = usge;
  110. s_ObjectIdField.SetValue(usge, objectId);
  111. castedObject = usge;
  112. return usge.CastTo<T>();
  113. }
  114. else
  115. {
  116. Debug.LogError($"Unable to evaluate type {typeInfo} : {jsonData}");
  117. }
  118. return null;
  119. }
  120. }
  121. public class UnknownTargetType : Target
  122. {
  123. public string jsonData;
  124. public UnknownTargetType() : base()
  125. {
  126. isHidden = true;
  127. }
  128. private List<BlockFieldDescriptor> m_activeBlocks = null;
  129. public UnknownTargetType(string displayName, string jsonData)
  130. {
  131. var split = displayName.Split('.');
  132. var last = split[split.Length - 1];
  133. this.displayName = last.Replace("Target", "") + " (Unknown)";
  134. isHidden = false;
  135. this.jsonData = jsonData;
  136. }
  137. public override void Deserailize(string typeInfo, string jsonData)
  138. {
  139. this.jsonData = jsonData;
  140. base.Deserailize(typeInfo, jsonData);
  141. }
  142. public override string Serialize()
  143. {
  144. return jsonData.Trim();
  145. }
  146. //When we first call GetActiveBlocks, we assume any unknown blockfielddescriptors are owned by this target
  147. public override void GetActiveBlocks(ref TargetActiveBlockContext context)
  148. {
  149. if (m_activeBlocks == null)
  150. {
  151. m_activeBlocks = new List<BlockFieldDescriptor>();
  152. foreach (var cur in context.currentBlocks)
  153. {
  154. if (cur.isUnknown && !string.IsNullOrEmpty(cur.displayName))
  155. {
  156. m_activeBlocks.Add(cur);
  157. }
  158. }
  159. }
  160. foreach (var block in m_activeBlocks)
  161. {
  162. context.AddBlock(block);
  163. }
  164. }
  165. public override void GetFields(ref TargetFieldContext context)
  166. {
  167. }
  168. public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, Action onChange, Action<string> registerUndo)
  169. {
  170. context.AddHelpBox(MessageType.Warning, "Cannot find the code for this Target, a package may be missing.");
  171. }
  172. public override bool IsActive() => false;
  173. public override void Setup(ref TargetSetupContext context)
  174. {
  175. }
  176. public override bool WorksWithSRP(RenderPipelineAsset scriptableRenderPipeline) => false;
  177. }
  178. private class UnknownSubTargetType : SubTarget
  179. {
  180. public string jsonData;
  181. public UnknownSubTargetType() : base()
  182. {
  183. isHidden = true;
  184. }
  185. public UnknownSubTargetType(string displayName, string jsonData) : base()
  186. {
  187. isHidden = false;
  188. this.displayName = displayName;
  189. this.jsonData = jsonData;
  190. }
  191. public override void Deserailize(string typeInfo, string jsonData)
  192. {
  193. this.jsonData = jsonData;
  194. base.Deserailize(typeInfo, jsonData);
  195. }
  196. public override string Serialize()
  197. {
  198. return jsonData.Trim();
  199. }
  200. internal override Type targetType => typeof(UnknownTargetType);
  201. public override void GetActiveBlocks(ref TargetActiveBlockContext context)
  202. {
  203. }
  204. public override void GetFields(ref TargetFieldContext context)
  205. {
  206. }
  207. public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, Action onChange, Action<string> registerUndo)
  208. {
  209. context.AddHelpBox(MessageType.Warning, "Cannot find the code for this SubTarget, a package may be missing.");
  210. }
  211. public override bool IsActive() => false;
  212. public override void Setup(ref TargetSetupContext context)
  213. {
  214. }
  215. }
  216. internal class UnknownShaderPropertyType : AbstractShaderProperty
  217. {
  218. public string jsonData;
  219. public UnknownShaderPropertyType(string displayName, string jsonData) : base()
  220. {
  221. this.displayName = displayName;
  222. this.jsonData = jsonData;
  223. }
  224. public override void Deserailize(string typeInfo, string jsonData)
  225. {
  226. this.jsonData = jsonData;
  227. base.Deserailize(typeInfo, jsonData);
  228. }
  229. public override string Serialize()
  230. {
  231. return jsonData.Trim();
  232. }
  233. internal override ConcreteSlotValueType concreteShaderValueType => ConcreteSlotValueType.Vector1;
  234. internal override bool isExposable => false;
  235. internal override bool isRenamable => false;
  236. internal override ShaderInput Copy()
  237. {
  238. // we CANNOT copy ourselves, as the serialized GUID in the jsonData would not match the json GUID
  239. return null;
  240. }
  241. public override PropertyType propertyType => PropertyType.Float;
  242. internal override void GetPropertyReferenceNames(List<string> result) { }
  243. internal override void GetPropertyDisplayNames(List<string> result) { }
  244. internal override string GetPropertyBlockString() { return ""; }
  245. internal override void AppendPropertyBlockStrings(ShaderStringBuilder builder)
  246. {
  247. builder.AppendLine("/* UNKNOWN PROPERTY: " + referenceName + " */");
  248. }
  249. internal override bool AllowHLSLDeclaration(HLSLDeclaration decl) => false;
  250. internal override void ForeachHLSLProperty(Action<HLSLProperty> action)
  251. {
  252. action(new HLSLProperty(HLSLType._float, referenceName, HLSLDeclaration.Global, concretePrecision));
  253. }
  254. internal override string GetPropertyAsArgumentString(string precisionString) { return ""; }
  255. internal override AbstractMaterialNode ToConcreteNode() { return null; }
  256. internal override PreviewProperty GetPreviewMaterialProperty()
  257. {
  258. return new PreviewProperty(propertyType)
  259. {
  260. name = referenceName,
  261. floatValue = 0.0f
  262. };
  263. }
  264. public override string GetPropertyTypeString() { return ""; }
  265. }
  266. internal class UnknownMaterialSlotType : MaterialSlot
  267. {
  268. // used to deserialize some data out of an unknown MaterialSlot
  269. class SerializerHelper
  270. {
  271. [SerializeField]
  272. public string m_DisplayName = null;
  273. [SerializeField]
  274. public SlotType m_SlotType = SlotType.Input;
  275. [SerializeField]
  276. public bool m_Hidden = false;
  277. [SerializeField]
  278. public string m_ShaderOutputName = null;
  279. [SerializeField]
  280. public ShaderStageCapability m_StageCapability = ShaderStageCapability.All;
  281. }
  282. public string jsonData;
  283. public UnknownMaterialSlotType(string displayName, string jsonData) : base()
  284. {
  285. // copy some minimal information to try to keep the UI as similar as possible
  286. var helper = new SerializerHelper();
  287. JsonUtility.FromJsonOverwrite(jsonData, helper);
  288. this.displayName = helper.m_DisplayName;
  289. this.hidden = helper.m_Hidden;
  290. this.stageCapability = helper.m_StageCapability;
  291. this.SetInternalData(helper.m_SlotType, helper.m_ShaderOutputName);
  292. // save the original json for saving
  293. this.jsonData = jsonData;
  294. }
  295. public override void Deserailize(string typeInfo, string jsonData)
  296. {
  297. this.jsonData = jsonData;
  298. base.Deserailize(typeInfo, jsonData);
  299. }
  300. public override string Serialize()
  301. {
  302. return jsonData.Trim();
  303. }
  304. public override bool isDefaultValue => true;
  305. public override SlotValueType valueType => SlotValueType.Vector1;
  306. public override ConcreteSlotValueType concreteValueType => ConcreteSlotValueType.Vector1;
  307. public override void AddDefaultProperty(PropertyCollector properties, GenerationMode generationMode) { }
  308. public override void CopyValuesFrom(MaterialSlot foundSlot)
  309. {
  310. // we CANNOT copy data from another slot, as the GUID in the serialized jsonData would not match our real GUID
  311. throw new NotSupportedException();
  312. }
  313. }
  314. [NeverAllowedByTarget]
  315. internal class UnknownNodeType : AbstractMaterialNode
  316. {
  317. public string jsonData;
  318. public UnknownNodeType() : base()
  319. {
  320. jsonData = null;
  321. isValid = false;
  322. SetOverrideActiveState(ActiveState.ExplicitInactive, false);
  323. SetActive(false, false);
  324. }
  325. public UnknownNodeType(string jsonData)
  326. {
  327. this.jsonData = jsonData;
  328. isValid = false;
  329. SetOverrideActiveState(ActiveState.ExplicitInactive, false);
  330. SetActive(false, false);
  331. }
  332. public override void OnAfterDeserialize(string json)
  333. {
  334. jsonData = json;
  335. base.OnAfterDeserialize(json);
  336. }
  337. public override string Serialize()
  338. {
  339. EnqueSlotsForSerialization();
  340. return jsonData.Trim();
  341. }
  342. public override void ValidateNode()
  343. {
  344. base.ValidateNode();
  345. owner.AddValidationError(objectId, "This node type could not be found. No function will be generated in the shader.", ShaderCompilerMessageSeverity.Warning);
  346. }
  347. // unknown node types cannot be copied, or else their GUID would not match the GUID in the serialized jsonDAta
  348. public override bool canCutNode => false;
  349. public override bool canCopyNode => false;
  350. }
  351. internal class UnknownGraphDataExtension : AbstractShaderGraphDataExtension
  352. {
  353. public string name;
  354. public string jsonData;
  355. internal override string displayName => name;
  356. internal UnknownGraphDataExtension() : base() { }
  357. internal UnknownGraphDataExtension(string displayName, string jsonData)
  358. {
  359. name = displayName;
  360. this.jsonData = jsonData;
  361. }
  362. public override void Deserailize(string typeInfo, string jsonData)
  363. {
  364. this.jsonData = jsonData;
  365. base.Deserailize(typeInfo, jsonData);
  366. }
  367. public override string Serialize()
  368. {
  369. return jsonData.Trim();
  370. }
  371. internal override void OnPropertiesGUI(VisualElement context, Action onChange, Action<string> registerUndo, GraphData owner)
  372. {
  373. var helpBox = new HelpBoxRow(MessageType.Info);
  374. helpBox.Add(new Label("Cannot find the code for this data extension, a package may be missing."));
  375. context.hierarchy.Add(helpBox);
  376. }
  377. }
  378. #endregion //Unknown Data Handling
  379. static readonly Dictionary<string, Type> k_TypeMap = CreateTypeMap();
  380. internal static bool isDeserializing;
  381. internal static readonly Dictionary<string, JsonObject> valueMap = new Dictionary<string, JsonObject>();
  382. static List<MultiJsonEntry> s_Entries;
  383. internal static bool isSerializing;
  384. internal static readonly List<JsonObject> serializationQueue = new List<JsonObject>();
  385. internal static readonly HashSet<string> serializedSet = new HashSet<string>();
  386. static JsonObject currentRoot = null;
  387. static Dictionary<string, Dictionary<string, string>> jsonBlobs = new Dictionary<string, Dictionary<string, string>>();
  388. static Dictionary<string, Type> CreateTypeMap()
  389. {
  390. var map = new Dictionary<string, Type>();
  391. foreach (var type in TypeCache.GetTypesDerivedFrom<JsonObject>())
  392. {
  393. if (type.FullName != null)
  394. {
  395. map[type.FullName] = type;
  396. }
  397. }
  398. foreach (var type in TypeCache.GetTypesWithAttribute(typeof(FormerNameAttribute)))
  399. {
  400. if (type.IsAbstract || !typeof(JsonObject).IsAssignableFrom(type))
  401. {
  402. continue;
  403. }
  404. foreach (var attribute in type.GetCustomAttributes(typeof(FormerNameAttribute), false))
  405. {
  406. var legacyAttribute = (FormerNameAttribute)attribute;
  407. map[legacyAttribute.fullName] = type;
  408. }
  409. }
  410. return map;
  411. }
  412. public static Type ParseType(string typeString)
  413. {
  414. k_TypeMap.TryGetValue(typeString, out var type);
  415. return type;
  416. }
  417. public static List<MultiJsonEntry> Parse(string str)
  418. {
  419. var result = new List<MultiJsonEntry>();
  420. const string separatorStr = "\n\n";
  421. var startIndex = 0;
  422. var raw = new FakeJsonObject();
  423. while (startIndex < str.Length)
  424. {
  425. var jsonBegin = str.IndexOf("{", startIndex, StringComparison.Ordinal);
  426. if (jsonBegin == -1)
  427. {
  428. break;
  429. }
  430. var jsonEnd = str.IndexOf(separatorStr, jsonBegin, StringComparison.Ordinal);
  431. if (jsonEnd == -1)
  432. {
  433. jsonEnd = str.IndexOf("\n\r\n", jsonBegin, StringComparison.Ordinal);
  434. if (jsonEnd == -1)
  435. {
  436. jsonEnd = str.LastIndexOf("}", StringComparison.Ordinal) + 1;
  437. }
  438. }
  439. var json = str.Substring(jsonBegin, jsonEnd - jsonBegin);
  440. JsonUtility.FromJsonOverwrite(json, raw);
  441. if (startIndex != 0 && string.IsNullOrWhiteSpace(raw.type))
  442. {
  443. throw new InvalidOperationException($"Type is null or whitespace in JSON:\n{json}");
  444. }
  445. result.Add(new MultiJsonEntry(raw.type, raw.id, json));
  446. raw.Reset();
  447. startIndex = jsonEnd + separatorStr.Length;
  448. }
  449. return result;
  450. }
  451. public static void Enqueue(JsonObject jsonObject, string json)
  452. {
  453. if (s_Entries == null)
  454. {
  455. throw new InvalidOperationException("Can only Enqueue during JsonObject.OnAfterDeserialize.");
  456. }
  457. valueMap.Add(jsonObject.objectId, jsonObject);
  458. s_Entries.Add(new MultiJsonEntry(jsonObject.GetType().FullName, jsonObject.objectId, json));
  459. }
  460. public static JsonObject CreateInstanceForDeserialization(string typeString)
  461. {
  462. if (!k_TypeMap.TryGetValue(typeString, out var type))
  463. {
  464. return new UnknownJsonObject(typeString);
  465. }
  466. var output = (JsonObject)Activator.CreateInstance(type, true);
  467. //This CreateInstance function is supposed to essentially create a blank copy of whatever class we end up deserializing into.
  468. //when we typically create new JsonObjects in all other cases, we want that object to be assumed to be the latest version.
  469. //This doesn't work if any json object was serialized before we had the idea of version, as the blank copy would have the
  470. //latest version on creation and since the serialized version wouldn't have a version member, it would not get overwritten
  471. //and we would automatically upgrade all previously serialized json objects incorrectly and without user action. To avoid this,
  472. //we default jsonObject version to 0, and if the serialized value has a different saved version it gets changed and if the serialized
  473. //version does not have a different saved value it remains 0 (earliest version)
  474. output.ChangeVersion(0);
  475. output.OnBeforeDeserialize();
  476. return output;
  477. }
  478. private static FieldInfo s_ObjectIdField =
  479. typeof(JsonObject).GetField("m_ObjectId", BindingFlags.Instance | BindingFlags.NonPublic);
  480. public static void Deserialize(JsonObject root, List<MultiJsonEntry> entries, bool rewriteIds)
  481. {
  482. if (isDeserializing)
  483. {
  484. throw new InvalidOperationException("Nested MultiJson deserialization is not supported.");
  485. }
  486. try
  487. {
  488. isDeserializing = true;
  489. currentRoot = root;
  490. root.ChangeVersion(0); //Same issue as described in CreateInstance
  491. for (var index = 0; index < entries.Count; index++)
  492. {
  493. var entry = entries[index];
  494. try
  495. {
  496. JsonObject value = null;
  497. if (index == 0)
  498. {
  499. value = root;
  500. }
  501. else
  502. {
  503. value = CreateInstanceForDeserialization(entry.type);
  504. }
  505. var id = entry.id;
  506. if (id != null)
  507. {
  508. // Need to make sure that references looking for the old ID will find it in spite of
  509. // ID rewriting.
  510. valueMap[id] = value;
  511. }
  512. if (rewriteIds || entry.id == null)
  513. {
  514. id = value.objectId;
  515. entries[index] = new MultiJsonEntry(entry.type, id, entry.json);
  516. valueMap[id] = value;
  517. }
  518. s_ObjectIdField.SetValue(value, id);
  519. }
  520. catch (Exception e)
  521. {
  522. // External code could throw exceptions, but we don't want that to fail the whole thing.
  523. // Potentially, the fallback type should also be used here.
  524. Debug.LogException(e);
  525. }
  526. }
  527. s_Entries = entries;
  528. // Not a foreach because `entries` can be populated by calls to `Enqueue` as we go.
  529. for (var i = 0; i < entries.Count; i++)
  530. {
  531. var entry = entries[i];
  532. try
  533. {
  534. var value = valueMap[entry.id];
  535. value.Deserailize(entry.type, entry.json);
  536. // Set ID again as it could be overwritten from JSON.
  537. s_ObjectIdField.SetValue(value, entry.id);
  538. value.OnAfterDeserialize(entry.json);
  539. }
  540. catch (Exception e)
  541. {
  542. if (!String.IsNullOrEmpty(entry.id))
  543. {
  544. var value = valueMap[entry.id];
  545. if (value != null)
  546. {
  547. Debug.LogError($"Exception thrown while deserialize object of type {entry.type}: {e.Message}");
  548. }
  549. }
  550. Debug.LogException(e);
  551. }
  552. }
  553. s_Entries = null;
  554. foreach (var entry in entries)
  555. {
  556. try
  557. {
  558. var value = valueMap[entry.id];
  559. value.OnAfterMultiDeserialize(entry.json);
  560. }
  561. catch (Exception e)
  562. {
  563. Debug.LogException(e);
  564. }
  565. }
  566. }
  567. finally
  568. {
  569. valueMap.Clear();
  570. currentRoot = null;
  571. isDeserializing = false;
  572. }
  573. }
  574. public static string Serialize(JsonObject mainObject)
  575. {
  576. if (isSerializing)
  577. {
  578. throw new InvalidOperationException("Nested MultiJson serialization is not supported.");
  579. }
  580. try
  581. {
  582. isSerializing = true;
  583. serializedSet.Add(mainObject.objectId);
  584. serializationQueue.Add(mainObject);
  585. var idJsonList = new List<(string, string)>();
  586. // Not a foreach because the queue is populated by `JsonData<T>`s as we go.
  587. for (var i = 0; i < serializationQueue.Count; i++)
  588. {
  589. var value = serializationQueue[i];
  590. var json = value.Serialize();
  591. idJsonList.Add((value.objectId, json));
  592. }
  593. if (jsonBlobs.TryGetValue(mainObject.objectId, out var blobs))
  594. {
  595. foreach (var blob in blobs)
  596. {
  597. if (!idJsonList.Contains((blob.Key, blob.Value)))
  598. idJsonList.Add((blob.Key, blob.Value));
  599. }
  600. }
  601. idJsonList.Sort((x, y) =>
  602. // Main object needs to be placed first
  603. x.Item1 == mainObject.objectId ? -1 :
  604. y.Item1 == mainObject.objectId ? 1 :
  605. // We sort everything else by ID to consistently maintain positions in the output
  606. x.Item1.CompareTo(y.Item1));
  607. const string k_NewLineString = "\n";
  608. var sb = new StringBuilder();
  609. foreach (var (id, json) in idJsonList)
  610. {
  611. sb.Append(json);
  612. sb.Append(k_NewLineString);
  613. sb.Append(k_NewLineString);
  614. }
  615. return sb.ToString();
  616. }
  617. finally
  618. {
  619. serializationQueue.Clear();
  620. serializedSet.Clear();
  621. isSerializing = false;
  622. }
  623. }
  624. public static void PopulateValueMap(JsonObject mainObject)
  625. {
  626. if (isSerializing)
  627. {
  628. throw new InvalidOperationException("Nested MultiJson serialization is not supported.");
  629. }
  630. try
  631. {
  632. isSerializing = true;
  633. serializedSet.Add(mainObject.objectId);
  634. serializationQueue.Add(mainObject);
  635. // Not a foreach because the queue is populated by `JsonRef<T>`s as we go.
  636. for (var i = 0; i < serializationQueue.Count; i++)
  637. {
  638. var value = serializationQueue[i];
  639. value.Serialize();
  640. valueMap[value.objectId] = value;
  641. }
  642. }
  643. finally
  644. {
  645. serializationQueue.Clear();
  646. serializedSet.Clear();
  647. isSerializing = false;
  648. }
  649. }
  650. }
  651. }