Here is the error message:
XmlException: Root element is missing.
System.Xml.XmlTextReaderImpl.Throw (System.Exception e) (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlTextReaderImpl.ThrowWithoutLineInfo (System.String res) (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlTextReaderImpl.ParseDocumentContent () (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlTextReaderImpl.Read () (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlTextReader.Read () (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlReader.MoveToContent () (at <3df1727680a4410c8922c5d71cde3a04>:0)
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderActorContainer.Read4_ActorCollection () (at <c06125c6813e448a84b3586be54913c5>:0)
System.Reflection.RuntimeMethodInfo.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <e40e5a8f982c4b618a930d29f9bd091c>:0)
Rethrow as InvalidOperationException: There is an error in XML document (0, 0).
System.Xml.Serialization.XmlSerializer.Deserialize (System.Xml.XmlReader xmlReader, System.String encodingStyle, System.Xml.Serialization.XmlDeserializationEvents events) (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.Serialization.XmlSerializer.Deserialize (System.Xml.XmlReader xmlReader, System.String encodingStyle) (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.Serialization.XmlSerializer.Deserialize (System.IO.Stream stream) (at <3df1727680a4410c8922c5d71cde3a04>:0)
GenshinImpactMovementSystem.SaveData.LoadActors (System.String path) (at Assets/GenshinImpactMovementSystem/Scripts/Characters/Player/Data/XML/SaveData.cs:54)
GenshinImpactMovementSystem.SaveData.Load (System.String path) (at Assets/GenshinImpactMovementSystem/Scripts/Characters/Player/Data/XML/SaveData.cs:19)
GenshinImpactMovementSystem.GameController <>c.<OnEnable>b__9_1 () (at Assets/GenshinImpactMovementSystem/Scripts/Controllers/GameController.cs:76)
UnityEngine.Events.InvokableCall.Invoke () (at <4a31731933e0419ca5a995305014ad37>:0)
UnityEngine.Events.UnityEvent.Invoke () (at <4a31731933e0419ca5a995305014ad37>:0)
UnityEngine.UI.Button.Press () (at Library/PackageCache/[email protected]/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/[email protected]/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/[email protected]/Runtime/EventSystem/ExecuteEvents.cs:57)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents EventFunction`1[T1] functor) (at Library/PackageCache/[email protected]/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/[email protected]/Runtime/EventSystem/EventSystem.cs:501)
I am not sure on what the error is but I think its because the xml file is blank but in the tutorial the code should already be written. There are 4 scripts that I am using to create the saving and loading system.
Actor script:
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using UnityEngine;
namespace GenshinImpactMovementSystem
{
public class Actor : MonoBehaviour
{
public ActorData data = new ActorData();
public string name = "actor";
public void StoreData()
{
data.name = name;
Vector3 pos = transform.position;
data.posX = pos.x;
data.posY = pos.y;
data.posZ = pos.z;
}
public void LoadData()
{
name = data.name;
transform.position = new Vector3(data.posX, data.posY, data.posZ);
}
void OnEnable()
{
SaveData.OnLoaded = delegate { LoadData(); };
SaveData.OnBeforeSave = delegate { StoreData(); };
SaveData.OnBeforeSave = delegate { SaveData.AddActorData(data); };
}
void OnDisable()
{
SaveData.OnLoaded -= delegate { LoadData(); };
SaveData.OnBeforeSave -= delegate { StoreData(); };
SaveData.OnBeforeSave -= delegate { SaveData.AddActorData(data); };
}
}
public class ActorData
{
[XmlAttribute("Name")]
public string name;
[XmlElement("PosX")]
public float posX;
[XmlElement("PosY")]
public float posY;
[XmlElement("PosZ")]
public float posZ;
}
}
Actor container script:
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using UnityEngine;
namespace GenshinImpactMovementSystem
{
[XmlRoot("ActorCollection")]
public class ActorContainer
{
[XmlArray("Actors")]
[XmlArrayItem("Actor")]
public List<ActorData> actors = new List<ActorData>();
}
}
Save data script:
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
using UnityEngine;
namespace GenshinImpactMovementSystem
{
public class SaveData
{
public static ActorContainer actorContainer = new ActorContainer();
public delegate void SerializeAction();
public static event SerializeAction onl oaded;
public static event SerializeAction OnBeforeSave;
public static void Load(string path)
{
actorContainer = LoadActors(path);
foreach (ActorData data in actorContainer.actors)
{
GameController.CreateActor(data, GameController.playerPath, new Vector3(data.posX, data.posY, data.posZ), Quaternion.identity);
}
onl oaded();
}
public static void Save(string path, ActorContainer actors)
{
OnBeforeSave();
SaveActors(path, actors);
ClearActors();
}
public static void AddActorData(ActorData data)
{
actorContainer.actors.Add(data);
}
public static void ClearActors()
{
actorContainer.actors.Clear();
}
private static ActorContainer LoadActors(string path)
{
XmlSerializer serializer = new XmlSerializer(typeof(ActorContainer));
FileStream stream = new FileStream(path, FileMode.Create);
ActorContainer actors = serializer.Deserialize(stream) as ActorContainer;
stream.Close();
return actors;
}
private static void SaveActors(string path, ActorContainer actors)
{
XmlSerializer serializer = new XmlSerializer(typeof(ActorContainer));
FileStream stream = new FileStream(path, FileMode.Open);
serializer.Serialize(stream, actors);
stream.Close();
}
}
}
Game controller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Cinemachine;
using TMPro;
namespace GenshinImpactMovementSystem
{
public class GameController : MonoBehaviour
{
public Button saveButton;
public Button loadButton;
public CinemachineVirtualCamera camera;
public const string playerPath = "Prefabs/Player";
private static string dataPath = string.Empty;
void Awake()
{
if (Application.platform == RuntimePlatform.IPhonePlayer)
{
dataPath = System.IO.Path.Combine(Application.persistentDataPath, "GenshinImpactMovementSystem/Resources/actors.xml");
}
else
{
dataPath = System.IO.Path.Combine(Application.dataPath, "GenshinImpactMovementSystem/Resources/actors.xml");
}
}
void Start()
{
CreateActor(playerPath, new Vector3(0, 0, 0), Quaternion.identity);
}
public static Actor CreateActor(string path, Vector3 position, Quaternion rotation)
{
GameObject prefab = Resources.Load<GameObject>(path);
GameObject go = GameObject.Instantiate(prefab, position, rotation) as GameObject;
Player playerScript = go.GetComponent<Player>();
Transform cameraLookAt = playerScript.cameraPointLookAt;
CinemachineVirtualCamera cam = FindObjectOfType<CinemachineVirtualCamera>();
cam.Follow = cameraLookAt;
cam.LookAt = cameraLookAt;
playerScript.CameraRecenteringUtility.VirtualCamera = cam;
Actor actor = go.GetComponent<Actor>() ?? go.AddComponent<Actor>();
return actor;
}
public static Actor CreateActor(ActorData data, string path, Vector3 position, Quaternion rotation)
{
GameObject prefab = Resources.Load<GameObject>(path);
GameObject go = GameObject.Instantiate(prefab, position, rotation);
Actor actor = go.GetComponent<Actor>() ?? go.AddComponent<Actor>();
actor.data = data;
return actor;
}
void OnEnable()
{
saveButton.onClick.AddListener(delegate {SaveData.Save(dataPath, SaveData.actorContainer);});
loadButton.onClick.AddListener(delegate {SaveData.Load(dataPath);});
}
void OnDisable()
{
saveButton.onClick.RemoveListener(delegate {SaveData.Save(dataPath, SaveData.actorContainer);});
loadButton.onClick.RemoveListener(delegate {SaveData.Load(dataPath);});
}
}
}
CodePudding user response:
XML well-formedness rules call for exactly one root element.
This example document is valid:
<doc>
<entry>...</entry>
<entry>...</entry>
</doc>
This example document is invalid because there's more than one top-level element:
<entry>...</entry>
<entry>...</entry>
CodePudding user response:
In constructing your FileStream
streams, you are using the wrong values for the FileMode
parameter. This enum has the following meanings:
FileMode
Meaning Append Opens the file if it exists and seeks to the end of the file, or creates a new file. Create Specifies that the operating system should create a new file. If the file already exists, it will be overwritten. CreateNew Specifies that the operating system should create a new file... If the file already exists, an IOException exception is thrown. Open Specifies that the operating system should open an existing file. OpenOrCreate Specifies that the operating system should open a file if it exists; otherwise, a new file should be created. Truncate Specifies that the operating system should open an existing file. When the file is opened, it should be truncated so that its size is zero bytes.
Thus your LoadActors()
should be:
private static ActorContainer LoadActors(string path)
{
XmlSerializer serializer = new XmlSerializer(typeof(ActorContainer));
using (var stream = new FileStream(path, FileMode.Open)) // Was FileMode.Create
{
return (ActorContainer)serializer.Deserialize(stream);
}
}
And SaveActors()
should be:
private static void SaveActors(string path, ActorContainer actors)
{
XmlSerializer serializer = new XmlSerializer(typeof(ActorContainer));
using (var stream = new FileStream(path, FileMode.Create)) // was FileMode.Open
{
serializer.Serialize(stream, actors);
}
}
Also, rather than closing the FileStream
manually, you should dispose of it via a using
statement. This ensures the stream is closed even if an exception is thrown.