I'm attempting to create a method for Unity3D that will allow me to populate a UI via an XML file. i.e. Rather than naming each button and label, they can carry generic names like "progress button" or "large text" Then using the C# script be matched to the verbose name in the XML file.
I have searched extensively for tutorials, examples and guides but each that I have found has been overkill for what I am trying to accomplish.
Ideally, I'd like to provide an XML file using the following structure in the XML file:
<?xml version="1.0" encoding="utf-8"?>
<strings>
<string name="progressBtn">Next</string>
<string name="reverseBtn">Back</string>
<string name="largeText">This is Large Text</string>
</strings>
I know how to dynamically change text in unity by accessing the properties of text-object's so I'm not worried about that step. What I have currently is this:
using UnityEngine;
using System.Collections;
using System.Xml;
using System.IO;
public class textParser : MonoBehaviour
{
public TextAsset targetXMLFile;
public GameObject uiObjectText;
string targetString;
// Use this for initialization
void Start ()
{
checkFile();//Check for strings file.
checkTarget(uiObjectText.name);//Check for the object name in the GUI object
}
// Update is called once per frame
void Update ()
{
//TODO
}
//Check for strings file.
void checkFile()
{
if (targetXMLFile == null) //If is Null, Log an Error
{
print("Error: target text file not loaded!");
}
else // If something, log the file name
{
print(targetXMLFile.name + " Target text file loaded!");
}
}
//Check for the object name in the GUI object
void checkTarget(string target)
{
if (target == null) //If is Null, Log an Error
{
print("Error: Unable to extract target ui object name!");
}
else// if something, Log the GUI Object name
{
print("Found: " + target + " In GUI.");
}
}
}
Obviously very basic, but it works. I know I need to use the XML libraries to accomplish my search (String matching I understand). Getting to that step is what eludes me.
Any tutorials that are much more towards this use of XML I'd love to look at, or if anyone could give me an idea of what methods I need to access to accomplish this. Personally ,I'd love to understand the verbose process behind what I am trying to do if anyone could provide a link to example code.
Thanks in Advance!
You can use the Xml.Serialization from .NET:
https://unitygem.wordpress.com/xml-serialisation/
Try something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string header = "<?xml version=\"1.0\" encoding=\"utf-8\"?><strings></strings>";
XDocument doc = XDocument.Parse(header);
XElement strings = (XElement)doc.FirstNode;
List<List<string>> buttons = new List<List<string>>() {
new List<string>() {"progressBTn", "Next"},
new List<string>() {"reverseBth", "Back"},
new List<string>() {"largeText", "This is Large Text"}
};
foreach(List<string> button in buttons)
{
strings.Add(
new XElement("string", new object[] {
new XAttribute("name", button[0]),
button[1]
}));
}
}
}
}
Related
I know that most of the time, the Deserialize method of XmlSerializer will complain if there's something wrong (for example, if there is a typo). However, I've found an example where it doesn't complain, when I would have expected it to; and I'd like to know if there's a way of being told about the problem.
The example code below contains 3 things: an good example which works as expected, and example which would complain (commented out) and an example which does not complain, which is the one I want to know how to tell that there is something wrong.
Note: I appreciate that one possible route would be XSD validation; but that really feels like a sledgehammer to crack what seems like a simpler problem. For example, if I was writing a deserializer which had unexpected data that it didn't know what to do with, I'd make my code complain about it.
I've used NUnit (NuGet package) for assertions; but you don't really need it, just comment out the Assert lines - you can see what I'm expecting.
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using NUnit.Framework;
public static class Program
{
public static void Main()
{
string goodExampleXml = #"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weather>Sunny</Weather></Weathers></Example>";
var goodExample = Load(goodExampleXml);
Assert.That(goodExample, Is.Not.Null);
Assert.That(goodExample.Weathers, Is.Not.Null);
Assert.That(goodExample.Weathers, Has.Length.EqualTo(1));
Assert.That(goodExample.Weathers.First(), Is.EqualTo(Weather.Sunny));
string badExampleXmlWhichWillComplainXml = #"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weather>Suny</Weather></Weathers></Example>";
// var badExampleWhichWillComplain = Load(badExampleXmlWhichWillComplainXml); // this would complain, quite rightly, so I've commented it out
string badExampleXmlWhichWillNotComplain = #"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weathe>Sunny</Weathe></Weathers></Example>";
var badExample = Load(badExampleXmlWhichWillNotComplain);
Assert.That(badExample, Is.Not.Null);
Assert.That(badExample.Weathers, Is.Not.Null);
// clearly, the following two assertions will fail because I mis-typed the tag name; but I want to know there has been a problem before this point.
Assert.That(badExample.Weathers, Has.Length.EqualTo(1));
Assert.That(badExample.Weathers.First(), Is.EqualTo(Weather.Sunny));
}
private static Example Load(string serialized)
{
byte[] byteArray = Encoding.UTF8.GetBytes(serialized);
var xmlSerializer = new XmlSerializer(typeof(Example));
using var stream = new MemoryStream(byteArray, false);
return (Example)xmlSerializer.Deserialize(stream);
}
}
public enum Weather
{
Sunny,
Cloudy,
Rainy,
Windy,
Stormy,
Snowy,
}
public class Example
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Serialized XML")]
[XmlArray("Weathers")]
[XmlArrayItem("Weather")]
public Weather[] Weathers { get; set; }
}
Having looked at Microsoft's published source code for XmlSerializer, it became apparent that there are events that you can subscribe to (which is what I was hoping for); but they aren't exposed on the XmlSerializer itself... you have to inject a struct containing them into the constructor.
So I've been able to modify the code from the question to have an event handler which gets called when an unknown node is encountered (which is exactly what I was after). You need one extra using, over the ones given in the question...
using System.Xml;
and then here is the modified "Load" method...
private static Example Load(string serialized)
{
XmlDeserializationEvents events = new XmlDeserializationEvents();
events.OnUnknownNode = (sender, e) => System.Diagnostics.Debug.WriteLine("Unknown Node: " + e.Name);
var xmlSerializer = new XmlSerializer(typeof(Example));
using var reader = XmlReader.Create(new StringReader(serialized));
return (Example)xmlSerializer.Deserialize(reader, events);
}
So now I just need to do something more valuable than just write a line to the Debug output.
Note that more events are available, as described on the XmlDeserializationEvents page, and I'll probably pay attention to each of them.
I tested following and it works
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =#"<?xml version=""1.0"" encoding=""utf-8"" ?>
<Example>
<Weathers>Sunny</Weathers>
<Weathers>Cloudy</Weathers>
<Weathers>Rainy</Weathers>
<Weathers>Windy</Weathers>
<Weathers>Stormy</Weathers>
<Weathers>Snowy</Weathers>
</Example>";
StringReader sReader = new StringReader(xml);
XmlReader reader = XmlReader.Create(sReader);
XmlSerializer serializer = new XmlSerializer(typeof(Example));
Example example = (Example)serializer.Deserialize(reader);
}
}
public enum Weather
{
Sunny,
Cloudy,
Rainy,
Windy,
Stormy,
Snowy,
}
public class Example
{
[XmlElement("Weathers")]
public Weather[] Weathers { get; set; }
}
}
A C# Windows application would like to load vector drawings that are stored in loose XAML files without allowing arbitrary code execution.
I am already loading such drawings from resources in linked assemblies over which I have control. However, I would like to also support loading loose XAML files. I imagine you can use XAML access control to limit the objects that can be instantiated in such XAML? Ideally, I would limit the loader to instantiating only the drawing primitives that are in the files we know about. It's ok that it would reject a file that has new drawing primitives in it that we have not whitelisted.
Is this a standard thing already supported by an API? Because I could not find it. Otherwise, does anyone have an example or beginnings of an example? This is for a free open source project and any help getting started would probably cut down the research I need to do by a lot.
The following seems to do a pretty decent job of white listing specific types in a XAML load:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Media;
using System.Xaml;
using System.Xml;
namespace TestXamlLoading
{
internal class SchemaContext : XamlSchemaContext
{
// map from XAML element name to required namespace (currently always the same)
private static readonly Dictionary<string, string> AllowedTypes = new Dictionary<string, string>();
static SchemaContext()
{
// questionable: <Image> is used in some drawing XAML, should review it
foreach (string name in new[]
{
"Canvas", "Compound", "Ellipse", "GradientStop", "GradientStopCollection", "Group", "Line",
"LinearGradientBrush", "MatrixTransform", "Path", "PathGeometry", "Polygon",
"RadialGradientBrush", "Rectangle", "RotateTransform", "ScaleTransform", "SkewTransform", "TextBlock",
"TransformGroup", "TranslateTransform"
})
{
AllowedTypes[name] = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
}
}
public SchemaContext(IEnumerable<Assembly> referenceAssemblies, XamlSchemaContextSettings settings) : base(
referenceAssemblies, settings)
{
// no code
}
protected override XamlType GetXamlType(string xamlNamespace, string name, params XamlType[] typeArguments)
{
if (!AllowedTypes.TryGetValue(name, out string requiredNamespace) || xamlNamespace != requiredNamespace)
{
throw new Exception($"disallowed instantiation of '{xamlNamespace}' '{name}' from XAML");
}
return base.GetXamlType(xamlNamespace, name, typeArguments);
}
}
internal class Program
{
[STAThreadAttribute]
private static void Main(string[] args)
{
bool shouldFail = TestLoad("..\\..\\..\\badfile.xaml");
Debug.Assert(!shouldFail);
bool shouldSucceed = TestLoad("..\\..\\..\\goodfile.xaml");
Debug.Assert(shouldSucceed);
}
private static bool TestLoad(string path)
{
Stream inputStream = new FileStream(path, FileMode.Open);
XmlReader xmlReader = new XmlTextReader(inputStream);
Assembly[] referenceAssemblies =
{
// these are two separate assemblies which contain all the types we allow
Assembly.GetAssembly(typeof(Canvas)),
Assembly.GetAssembly(typeof(TransformGroup))
};
XamlSchemaContextSettings settings = new XamlSchemaContextSettings();
XamlSchemaContext schemaContext = new SchemaContext(referenceAssemblies, settings);
try
{
XamlReader reader = new XamlXmlReader(xmlReader, schemaContext);
Canvas canvas = (Canvas) System.Windows.Markup.XamlReader.Load(reader);
}
catch (Exception e)
{
Debug.WriteLine(e);
return false;
}
return true;
}
}
}
I am using coded ui automation in Visual Studio 2012. So I have a requirement where I have to write some generic/common methods which can be used with any type of UI control.
I an new to coded ui. How to write such methods so that i can reuse it in code.
Basically, for example I want common methods for verifying certain control/Tiles/TAB/Objects is visible or not on UI.
NOTE:- I found below solution from some blog, might be this could be helpful.
To replace the UIMap, we are going to use the power of Generics in the .Net Framework. For ease of use in our testing, I built a static extension so that I could attach it to any UITestControl and use a SearchFor method to find things. As you can see in my code below, it is pretty straight forward. I use dynamics to pass in variables that I can use for both the SearchProperties and FilterProperties to go find items in the application. If you need to find more information on the controls you can use the “Coded UI Test Builder” to get more detailed information.
using Microsoft.VisualStudio.TestTools.UITesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Blog.Example
{
public static class CodedUIExtension
{
public static T SearchFor<T>(this UITestControl _this, dynamic searchProperties, dynamic filterProperties = null) where T : UITestControl, new()
{
T ctrl = new T();
ctrl.Container = _this;
IEnumerable<string> propNames = ((object)searchProperties).GetPropertiesForObject();
foreach (var item in propNames)
{
ctrl.SearchProperties.Add(item, ((object)searchProperties).GetPropertyValue(item).ToString());
}
if (filterProperties != null)
{
propNames = ((object)filterProperties).GetPropertiesForObject();
foreach (var item in propNames)
{
ctrl.FilterProperties.Add(item, ((object)filterProperties).GetPropertyValue(item).ToString());
}
}
return ctrl as T;
}
private static IEnumerable<string> GetPropertiesForObject(this object _this)
{
return (from x in _this.GetType().GetProperties() select x.Name).ToList();
}
private static object GetPropertyValue(this object _this, string propName)
{
var prop = (from x in _this.GetType().GetProperties() where x.Name == propName select x).FirstOrDefault();
return prop.GetValue(_this);
}
}
}
Next, let’s see how to use this in a test. You can just start out with a basic class template using the File –> New in Visual Studio. You will see in my example below that I have added some “using” statements to bring in the testing framework while also adding some attributes for CodedUITest and TestMethod. Once you have these in place you can get started writing your test. Since we are not using the Coded UI Test Generator here, we will go ahead and add the items we need on our own. Not only are we going to remove the brittleness here but we are also going to be efficient.
The first thing I do in my test is to open a Browser and go to a url. In my case I am going to our blog. This will open up the browser and navigate all in one line (nifty). Next we are going to start to find the items we need to interact with on the screen. As you can see the first item I am going to go get n my test is the header section. This is a custom html control with the tagname “HEADER.” Once I find this control, I am going to use it in the next section so that I have a container to search within. To be honest, I really don’t have to do this, but I did want to show you that you can search for items using a container instead of having to search the entire DOM every time. Next I will get the search button control and use the “Mouse.Click” operation to click and open the search text box. Once the Coded UI Test finds the search box, it automates the text I want and use and then uses keyboard controls to send the ENTER key for the search.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.VisualStudio.TestTools.UITest.Extension;
using Microsoft.VisualStudio.TestTools.UITesting;
using Microsoft.VisualStudio.TestTools.UITesting.WinControls;
using Microsoft.VisualStudio.TestTools.UITesting.HtmlControls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard;
using Mouse = Microsoft.VisualStudio.TestTools.UITesting.Mouse;
using MouseButtons = System.Windows.Forms.MouseButtons;
namespace Blog.Example
{
[CodedUITest]
public class SimpleUITestWithoutCodedUI
{
[TestMethod]
public void SimpleWebTest()
{
//open the default browser and navigate to a web page - blog.falafel.com
BrowserWindow browser = BrowserWindow.Launch(new Uri("http://blog.falafel.com"));
//we can use controls as a way to narrow down the search for other controls
var headerSection = browser.SearchFor<HtmlCustom>(new { TagName = "HEADER" });
//let's search for something
var searchIcon = headerSection.SearchFor<HtmlHyperlink>(new { href = "http://blog.falafel.com/#searchbox" });
Mouse.Click(searchIcon);
var searchInput = browser.SearchFor<HtmlEdit>(new { name = "s" }, new { type = "INPUT" });
searchInput.Text = "treats and tricks";
Keyboard.SendKeys("{ENTER}");
//find our searched for item
var postToLookFor = browser.SearchFor<HtmlCustom>(new { TagName = "ARTICLE" }, new { InnerText = "31 Days of Visual Studio 2015 Tricks and Treats Blog Post" });
//validate our search
Assert.IsTrue(postToLookFor.Exists);
}
}
}
The next thing I do on the search result screen is find the item that I am interested in evaluating. I made that sound really easy, but having dynamic controls show up and find them is one of the hardest things to do in any Testing Framework. With the power of C# and the flexibility of the Coded UI Framework, I am able to dynamically find my items using search and filter properties so that I can evaluate them. The rest of the test, including the assertion, are basic unit testing techniques.
Like that?
public bool IsVisible(Control control)
{
return control.Visible;
}
I implemented a generic search function as such:
public static ControlType IdentifyControlByIdentifierEqualsValue<ControlType>(UITestControl parent, ControlIdentifier identifierTypeAndValue) where ControlType : UITestControl
{
var control = (ControlType)Activator.CreateInstance(typeof(ControlType), new UITestControl[] { parent });
control.SearchProperties.Add(identifierTypeAndValue.Type, identifierTypeAndValue.Value);
var wasFound = control.TryFind();
if (!wasFound)
throw new UITestControlNotFoundException("The control of type " + control.GetType().ToString() + " with the identifier type of " + identifierTypeAndValue.Type + " and the identifying attribute of " + identifierTypeAndValue.Value + " was not able to be found");
return control;
}
With the supporting class
public class ControlIdentifier
{
public ControlIdentifier(string type, string id)
{
Type = type;
Value = id;
}
public string Type { get; set; }
public string Value { get; set; }
}
Called as such:
IdentifyControlByIdentifierEqualsValue<WpfTabList>(workWindow,
new ControlIdentifer(WpfButton.PropertyNames.Name, "OK");
I am making a program updater / launcher that can be used for any program.
I have a config file on the client and a config file on a http server. I get version numbers from both of them and compare them and if they are are not = then update the client.
I have everything working except for when the update starts. What I need is say if someone downloaded my application and do not use if for say a month and in between that time I have 5 or so updates.
The problem is how to I get my program to download the first update , install it and then download the next update untill they have all been downloaded?
I am new to programming and this is the only kind of app I can think of to work on to learn.
Thanks
My settings.conf on http server XML File.
<Table>
<Product>
<Product_id>1</Product_id>
<Product_name>Infected</Product_name>
<Product_version>1.0.0.1</Product_version>
<Product_Url>http://localhost/update/v1.0.0.1.exe</Product_Url>
<Product_id>2</Product_id>
<Product_name>Infected</Product_name>
<Product_version>1.0.0.2</Product_version>
<Product_Url>http://localhost/update/v1.0.0.2.exe</Product_Url>
<Product_id>3</Product_id>
<Product_name>Infected</Product_name>
<Product_version>1.0.0.3</Product_version>
<Product_Url>http://localhost/update/v1.0.0.3.exe</Product_Url>
<Product_id>4</Product_id>
<Product_name>Infected</Product_name>
<Product_version>1.0.0.4</Product_version>
<Product_Url>http://localhost/update/v1.0.0.4.exe</Product_Url>
<Product_id>5</Product_id>
<Product_name>Infected</Product_name>
<Product_version>1.0.0.5</Product_version>
<Product_Url>http://localhost/update/v1.0.0.5.exe</Product_Url>
</Product>
</Table>
My Client Config XML file.
<Table>
<Product>
<Product_id>1</Product_id>
<Product_name>Infected</Product_name>
<Product_version>1.0.0.0</Product_version>
<Product_Url>http://localhost/update/v1.0.0.1.exe</Product_Url>
</Product>
</Table>
My C# Form.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using System.Net;
using System.Diagnostics;
using System.Runtime.Remoting;
namespace Launcher
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public string localversion { get; set; }
public string remoteversion { get; set; }
public string UpdateURL { get; set; }
private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.Navigate("http://www.kceoc.com/");
webBrowser2.Navigate("http://www.kceoc.com/");
button1.Enabled = false; // Disable the launch button untill all updates are completed.
GetLocalXMLFile(); //Run first xml function to start everything off.
}
private void GetLocalXMLFile()
{
try //Start error checking.
{
using (XmlTextReader localxml = new XmlTextReader("settings.conf")) //Load xml file in same folder as launcher.exe
{
while (localxml.Read()) // Start reading the settings.conf file
{
switch (localxml.NodeType) //Get the Node that we will use.
{
case XmlNodeType.Text:
label1.Text = localxml.Value; //Change the text of label1 to value of Node.
string localversion = localxml.Value; // Store Node Value in string localversion for latter use.
GetRemoteXMLFile(localversion, remoteversion); //Everything went ok and got a value from Node so pass this all to our next function witch is get remote xml.
break;
}
}
}
}
catch (FileNotFoundException)
{
label1.Text = "Local Config not found. Reinstall the application"; // Catch error incase file is not there.
}
}
private void GetRemoteXMLFile(string localversion, string remoteversion)
{
try //Start error checking
{
using (XmlTextReader remotexml = new XmlTextReader("http://localhost/update/settings.conf")) //Load up remote xml on web server
{
while (remotexml.Read()) //Start reading xml file from server.
{
switch (remotexml.NodeType)
{
case XmlNodeType.Text:
label2.Text = remotexml.Value; // Change value of label2 to remote xml node value
remoteversion = remotexml.Value; // Set the remoteversion string to remotexml.value
CompareXMLFileVersions(localversion, remoteversion); // Everything went ok so send localversion string and remoteversion string to compare function.
break;
}
}
}
}
catch (FileNotFoundException)
{
label1.Text = "Remote config not found. Maby website id down?"; // Catch error incase file is not there.
}
}
private void CompareXMLFileVersions(string localversion, string remoteversion)
{
label1.Text = localversion; // Just so we can see the value in the lables to konw if they have value or not.
label2.Text = remoteversion; // Just so we can see the value in the lables to konw if they have value or not.
if (localversion == remoteversion) // Comparing the values of localversion and remoteversion and if they have same value then
{ // change label3 to You have latest version.
label3.Text = "You have the latest version";
}
else
{
label3.Text = "There is a new version. Starting update process here"; // If localversion and remoteversion are diffrent then let user know the files are out of date and start the updating process..
GetListOfUpdates(remoteversion); // Starting the updating process function..
}
}
private void GetListOfUpdates(string remoteversion)
{
//WebClient webClient = new WebClient();
//webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
//webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
//webClient.DownloadFileAsync(new Uri(remoteversion), #"v1.0.0.1.exe");
string url = "http://localhost/update/v1.0.0.1.exe";
WebClient downloader = new WebClient();
downloader.DownloadFileCompleted += new AsyncCompletedEventHandler(downloader_DownloadFileCompleted);
downloader.DownloadProgressChanged += new DownloadProgressChangedEventHandler(downloader_DownloadProgressChanged);
downloader.DownloadFileAsync(new Uri(url), "temp.exe");
}
void downloader_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
label1.Text = e.BytesReceived + " " + e.ProgressPercentage;
}
void downloader_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
//if (e.Error != null)
// MessageBox.Show(e.Error.Message);
//else
// MessageBox.Show("Completed!!!");
}
}
}
Thanks
Welcome to SO!
I am new to programming and this is the only kind of app I can think of to work on to learn. Thanks
Depending on how new you are, I'd really recommend you start with something a little easier. Otherwise, the first thing I'd recommend you do is to actually draw a flowchart. Your logic looks a little off, and it looks as if you're huffing it trying to design this system as you write it, which is something you never want to be doing.
There are a lot of solutions for this that provide a better, more reliable systems then anything you can make yourself, but I can understand the educational value of this sort of project. I made my own 'auto-update/launcher' recently for just that reason, and it works reasonably well, albeit on a free webserver with myself an some friends as the only users.
Here's the flowchart I made for it:
Large:http://i.imgur.com/qS1U8.png
This is actually the second iteration of my little project, with the first being less then overwhelming and somewhat disastrous in uncommon circumstances, but it's a good learning experiance. This one also has goofy command files that I can define things like showing messages to the user during an update, which is nice.
If you don't mind looking at terrible and messy code, you can look through the code repo here, although it's not documented and a few part's aren't actually used but haven't been removed from source control. An example application that uses it is here (source, also messy).
Sorry for what looks like a shameless self-plug, but I can't really answer your question directly and hope that you might be able to make use of some of this as an indication of how you should go about doing this, since it's actually a pretty fun project.
The whole error message is here:
There was an error while deserializing intermediate XML. Cannot find type
"WindowsGame1.Tile".
I'm making an XNA game, and right now I'm trying to load in a tile-based level from an xml file. Right now, my xml file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<XnaContent>
<Asset Type="WindowsGame1.Tile">
<Tile>
<X>0</X>
<Y>0</Y>
<ImageName>grass-tile</ImageName>
</Tile>
</Asset>
</XnaContent>
And my C# file looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace WindowsGame1
{
public class Tile
{
public int x;
public int y;
public string imageName;
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
public string ImageName
{
get { return imageName; }
set { imageName = value; }
}
}
}
I have a class that uses the XPath libraries to parse the xml file and make Tile's out of it, but it's all commented out right now because the xml stuff doesn't work anyways. In my Game1 class I tried importing:
using WindowsGame1.Tile;
and referencing:
Tile tile;
This tile class, as per a suggestion I found while trying to find a solution to this, but I'm still getting the same error, and my game won't build. Anyone know what I need to change to get this XML stuff working?
Here's what the code that should be parsing the XML should look like, though at the moment, it's all commented out until I can get it to compile.
namespace WindowsGame1
{
class ActionScreen : GameScreen
{
public ActionScreen(Game game, SpriteBatch spriteBatch, String file, AnimatedSprite sprite)
: base(game, spriteBatch)
{
this.file = file;
this.sprite = sprite;
initializeTiles();
}
private void initializeTiles()
{
XPathDocument doc = new XPathDocument(file);
XPathNavigator nav = doc.CreateNavigator();
XPathNodeIterator iter = nav.Select("tile");
while (iter.MoveNext())
{
int x, y;
string fileName;
x = int.Parse(iter.Current.GetAttribute("X", ""));
y = int.Parse(iter.Current.GetAttribute("Y", ""));
fileName = iter.Current.GetAttribute("ImageName", "");
Console.WriteLine(x + " " + y + " " + fileName);
}
}
}
}
It looks like you're trying to use the XNA content pipeline to build the XML file as content - so my answer will skip all the XPath stuff you metioned - you don't need it.
The "deserializing" that the error is referring to is when the content pipeline is trying to convert your XML into an object instance. It then takes that object and writes it out to an XNB file. Later on, when your game runs it will load that XNB file back into an object instance.
An instance of which object? Well, in your case, a WindowsGame1.Tile object. How does the content pipeline know what a WindowsGame1.Tile object is? It doesn't - not unless you tell it about it - hence the error message.
How do you fix this?
You need to reference the assembly that contains the type WindowsGame1.Tile from your content pipeline project.
You can't reference your game project directly - because that would create a circular dependency (your game project already depends on the content project).
Therefore you must put the type WindowsGame1.Tile into a separate assembly (create a new "Game Library Project") that you reference from both your game project and your content project (right click the content project, add reference, add project reference).
Then you can load your tile in-game via the content manager:
Tile myTile = Content.Load<Tile>("assetName");
If you get another error about the format of your XML (I think I spy an error, but I haven't checked), you can figure out what it is supposed to be by creating a dummy instance of your Tile type, then use IntermediateSerializer.Serialize to generate the appropriate XML. Here is an example of how to do this.
To make it deserialize properly, you have to have your properties names same in XML and in actual class. Try to rename property "imageName" to "image" in your Tile class.
The issue is you are using GetAttribute() method instead doing a Select("X").Value. For example, instead of
x = int.Parse(iter.Current.GetAttribute("X", ""));
try
x = int.Parse(iter.Current.Select("X").Value, string.Empty));
You also may want to consider the System.Xml.Serlization.XmlSerializer class since it is easier to use but I do not know whether it is included in the version of the XNA framework you are using.