2
\$\begingroup\$

got another question here. Its not about actually writing/reading files in XML, i already know how to-do this. But rather something that has bugged me for awhile. Ok, so when i write out my files, they look like this:

<?xml version="1.0" encoding="utf-8"?>
<XnaContent>
 <Asset Type="ProjectWitch.Animation">
      <Filename>C:\Users\Daniel Randell\Desktop\Project Witch\Project Witch (MAIN)\Project_Witch (MAIN)\Project_Witch (MAIN)Content\player.png</Filename>
      <CurrentFrame>0</CurrentFrame>
      <Number_Of_Frames>1</Number_Of_Frames>
      <Max_Time_Per_Frame>0</Max_Time_Per_Frame>
      <Animate_Once>0</Animate_Once>
      <Using_Image>1</Using_Image>
      <Rect>
        0    166  64   58   
      </Rect>
      <Vector2>
        12   12   
      </Vector2>
      <Rect>
        81   71   51   59   False
        80   71   13   43   True 
        121  71   12   22   True 
        130  82   7    41   True 
        89   63   11   23   True 
      </Rect>
 </Asset Type="ProjectWitch.Animation">

As you can see the problem, it should write out at the end:

      </Asset>
 </XnaContent>

But it's always writes out the:

 </Asset Type="ProjectWitch.Animation"> 

Its a bit annoying to keep having to look into the XML files and deleting this, so i'm wondering what i'm doing wrong?

Saving Code: http://pastebin.com/yyC3q3aS

Thanks for any help :).

\$\endgroup\$

1 Answer 1

6
\$\begingroup\$

You wrote:

writer.WriteStartElement("Asset Type=\"ProjectWitch.Animation\"");

That is not how you add an attribute using XmlTextWriter. I think the correct would be:

writer.WriteStartElement("Asset");
writer.WriteAttributeString("Type", "ProjectWitch.Animation");

And make sure you're not missing any WriteEndElement call.

By the way, the most recent and recommended method of handling XML in C# is the XDocument API. You should consider making the switch, because it works at an higher level, and you don't need to manually start and end your tags. Example:

// Create content
var root = new XElement("Player",         // Create root node
    new XAttribute("Id", "1"),            // Add attribute
    new XElement("Name", "Jon Skeet")     // Add child node with value
);

// Save to disk
XDocument document = new XDocument(root);
document.Save("file.xml");

Which would produce the following XML:

<?xml version="1.0" encoding="utf-8"?>
<Player Id="1">
    <Name>Jon Skeet</Name>
</Player>

Edit 1

Just wondering though with using the XDocument API, as you can see i'm saving alot of data, more importantly the Rectangles & Vector2s. Its quite fiddly i find to save these currently and i don't want to have a new element for each field i.e X/Y/Width/Height but rather like it is currently. Is that possible using the XDocument Api?

In the comments you raised concerns about being able to do something like this:

  <Rect>
    81   71   51   59
    80   71   13   43
    121  71   12   22
    130  82   7    41
    89   63   11   23
  </Rect>

You were using methods such as XmlTextWriter.WriteValue and XmlTextWriter.WriteWhiteSpace to do this incremental sort of writing. As far as I know you can't write to an XElement this way, but coupled with a StringBuilder object (which you can reuse) it's easy to get a similar behavior:

var stringBuilder = new StringBuilder(); // Create only once

stringBuilder.Clear();
foreach(var r in rectangles)
{
    stringBuilder.AppendLine(
        String.Format("{0}\t{1}\t{2}\t{3}", r.X, r.Y, r.Width, r.Height)
    );
}
var rectElement = new XElement("Rect", stringBuilder.ToString());

The StringBuilder has both Append and AppendLine methods which will still allow you to construct your values similarly to what you were doing before.

But there are many other ways to do this, some more efficient, others easier. Personally, I would do:

public static string SerializeRectangle(Rectangle r)
{
    return String.Format("{0}\t{1}\t{2}\t{3}", r.X, r.Y, r.Width, r.Height);
}

var rectElement = new XElement("Rect", String.Join("\n", rectangles.Select(SerializeRectangle)));

Edit 2

Last comment, in terms of reading the file, how would that work?

Here's a simple loading example without error handling. In a real scenario you should always check if Element or Attribute returns null:

var document = XDocument.Load("file.xml");
var player = document.Element("Player");
int id = int.Parse(player.Attribute("Id").Value);
string name = player.Element("Name").Value;

If there's more than one element with a certain name, use the Elements method instead to retrieve all of them at once.

\$\endgroup\$
19
  • \$\begingroup\$ Thanks again Dave! Just wondering though with using the XDocument API, as you can see i'm saving alot of data, more importantly the Rectangles & Vector2s. Its quite fiddly i find to save these currently and i don't want to have a new element for each field i.e X/Y/Width/Height but rather like it is currently. Is that possible using the XDocument Api? \$\endgroup\$
    – dan369
    Jan 8, 2012 at 18:30
  • 1
    \$\begingroup\$ Sure I don't see any limitations in using XDocument. There's two options. Either you save them as attributes, e.g. <Rectangle X="0" Y="0" Width="100" Height="100" />. Or you serialize/deserialize them into string, e.g. <Rectangle>0,0,100,100</Rectangle> and doing a string.Split(',') when reading it back. \$\endgroup\$ Jan 8, 2012 at 18:35
  • \$\begingroup\$ Just looked again at your example, and just in case the previous comment wasn't clear enough, I'll edit in my post a little example of how you could proceed. \$\endgroup\$ Jan 8, 2012 at 18:43
  • \$\begingroup\$ Thanks again :), Really liking XDocument already, a-lot more straight-forward to use. \$\endgroup\$
    – dan369
    Jan 8, 2012 at 18:54
  • \$\begingroup\$ Quickly, last comment, in terms of reading the file, how would that work? \$\endgroup\$
    – dan369
    Jan 8, 2012 at 18:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .