0
votes

I've been doing a quick challenge I set myself; make a generic XML (de)serializer. I'm having trouble with the I/O part of this. Code is as follows:

using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using UnityEngine;

public static class XML
{
    static readonly Dictionary<string, XmlSerializer> PathSerializers = new Dictionary<string, XmlSerializer>(); 

    public static T DeserializeXML<T>(string xmlPath)
    {
        var serializer = GetSerializer<T>(xmlPath);
        var xml = File.ReadAllText(GetFullPath(xmlPath));
        using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
        {
            return (T) serializer.Deserialize(stream);
        }
    }

    public static void SerializeXML<T>(T obj, string xmlPath, bool append)
    {
        using (var text = new StreamWriter(GetFullPath(xmlPath), append, Encoding.ASCII))
        {
            GetSerializer<T>(xmlPath).Serialize(text, obj);
        }
    }

    private static XmlSerializer GetSerializer<T>(string relativePath)
    {
        return PathSerializers.ContainsKey(relativePath) ? PathSerializers[relativePath] : AddSerializer<T>(relativePath);
    }

    private static string GetFullPath(string relativePath)
    {
        return Path.Combine(Application.dataPath, relativePath);
    }

    private static XmlSerializer AddSerializer<T>(string relativePath)
    {
        if (File.Exists(GetFullPath(relativePath)))
        {
            File.Create(GetFullPath(relativePath));
        }

        if (PathSerializers.ContainsKey(relativePath))
        {
            Debug.Log("Loaded cached serializer");
            return GetSerializer<T>(relativePath);
        }

        var serializer = new XmlSerializer(typeof(T));
        PathSerializers.Add(relativePath, serializer);
        return serializer;
    }
}

It might be me doing some crazy rookie mistake, but I simply cannot see it.

The methods are called from another script, and called on a class that I know is set up correctly.

Clarification:

If I serialize and then deserialize the same data I get an error as follows:

IOException: Sharing violation on path C:\Dev\C#\Projects\XML\Assets\leaderboard.xml System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/FileStream.cs:320) System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize) (wrapper remoting-invoke-with-check) System.IO.FileStream:.ctor (string,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare,int) System.IO.File.Create (System.String path, Int32 bufferSize) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/File.cs:135) System.IO.File.Create (System.String path) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/File.cs:130) XML.AddSerializer[Leaderboard] (System.String relativePath) (at Assets/XMLLoader.cs:43) XML.GetSerializer[Leaderboard] (System.String relativePath) (at Assets/XMLLoader.cs:31) XML.SerializeXML[Leaderboard] (.Leaderboard obj, System.String xmlPath, Boolean append) (at Assets/XMLLoader.cs:25) XMLTest.Start () (at Assets/XMLTest.cs:14)

2
Whats exactly your problem?Bgl86
@Bgl86 I added a clarification.Herbstein
Looks like the file is already opened by another application.Dbuggy
You could try adding a text.Close() in the SerializeXML to ensure the application closed the file after serializingDbuggy
@Dbuggy It's the same error with the text.Close added. I've tried making a dummy file in place and the error persists indepedent of that. The only place it could be open is in the Unity Editor, and I REALLY hope the editor isn't that stupid. Edit I tried building the project and the error persists.Herbstein

2 Answers

2
votes

I think it's a simple oversight. You are trying to create the file if it exists!. Just reverse the condition and it should be fine:

if (!File.Exists(GetFullPath(relativePath)))
{
    File.Create(GetFullPath(relativePath));
}
1
votes

For clarification this is what i use for serializing / deserializing xml with.

Perhaps with modifications it will help you.

/// <summary>
/// Class containing a load and store method to store a datafile as XML format.
/// </summary>
/// <typeparam name="T">The root object containing the data</typeparam>
public class DataFile<T> where T : new()
{
    /// <summary>
    /// Load the XML from Path
    /// </summary>
    /// <param name="path">Path to a XML file containing the data for object of type T</param>
    /// <returns>An object of type T as represented by the XML. Will return default(T) when the </returns>
    public static T Load(string path)
    {
        if (!File.Exists(path)) return default(T);
        var ser = new XmlSerializer(typeof(T));
        using (var stream = File.OpenRead(path))
        {
            var reader = new XmlTextReader(stream);
            if (!ser.CanDeserialize(reader))
                return default(T); // return null indicating the file can not be loaded

            return (T) ser.Deserialize(reader);
        }
    }

    /// <summary>
    /// Stores an object of type T as XML into a file
    /// </summary>
    /// <param name="path">A path to a file which should be written.</param>
    /// <param name="data">The object to be written to the given file</param>
    public static void Store(string path, T data)
    {
        var ser = new XmlSerializer(typeof(T));
        using (var stream = File.OpenWrite(path))
        {
            // Clear the file and write new contents.
            stream.SetLength(0);
            ser.Serialize(stream, data);
        }
    }
}