Reputation: 65
I'm new to working with xml and I have this xml file:
<?xml version="1.0" encoding="utf-8"?>
<Colonists>
<ColonistAmount amount="3" />
<Colonist num="1">
<BasicInfo Name="Susumu Croc Andersen" />
<BasicInfo Age="22" />
<BasicInfo Gender="1" />
<BasicInfo Trait1="Claustrophobic" />
<BasicInfo Trait2="Truthful" />
<Graphics BodyType="Thin" />
<Graphics SkinColor="0.9490196,0.9294118,0.8784314,1" />
<Graphics CrownType="Narrow" />
<Graphics HeadGraphicPath="Things/Pawn/Humanoid/Heads/Male/Male_Narrow_Pointy" />
<Graphics HairColor="0.9294118,0.7921569,0.6117647,1" />
<HairDef DefName="Wavy" />
<HairDef GraphicPath="Things/Pawn/Humanoid/Hairs/Wavy" />
<HairDef HairGender="Any" />
<HairTags Tag0="Urban" />
<HairTags Tag1="Rural" />
<HairDef Label="Wavy" />
<HairDef ShortHash="9521" />
<HairDef Selected="True" />
<Apparel>
<OnSkin Layer="OnSkin" Label="Button-down shirt" GraphicPath="Things/Pawn/Humanoid/Apparel/ShirtButton/ShirtButton" Color="1,0,0,1" />
<Shell Layer="Shell" Label="Duster" GraphicPath="Things/Pawn/Humanoid/Apparel/Duster/Duster" Color="1,0,0,1" />
</Apparel>
<Backstories>
<Childhood Index="190" />
<Adulthood Index="76" />
</Backstories>
<SkillPool Amount="40" />
<Skills Name="Construction" Value="0" Passion="1" />
<Skills Name="Growing" Value="0" Passion="2" />
<Skills Name="Research" Value="0" Passion="3" />
<Skills Name="Mining" Value="0" Passion="0" />
<Skills Name="Shooting" Value="0" Passion="0" />
<Skills Name="Melee" Value="0" Passion="0" />
<Skills Name="Social" Value="0" Passion="0" />
<Skills Name="Cooking" Value="0" Passion="0" />
<Skills Name="Medicine" Value="0" Passion="0" />
<Skills Name="Artistic" Value="0" Passion="0" />
<Skills Name="Crafting" Value="0" Passion="0" />
<End />
</Colonist>
<Colonist num="2">
<BasicInfo Name="Ignat Sir Juarez" />
<BasicInfo Age="27" />
<BasicInfo Gender="1" />
<BasicInfo Trait1="Careful" />
<BasicInfo Trait2="No self-awareness" />
<Graphics BodyType="Male" />
<Graphics SkinColor="1,0.9372549,0.7411765,1" />
<Graphics CrownType="Average" />
<Graphics HeadGraphicPath="Things/Pawn/Humanoid/Heads/Male/Male_Average_Normal" />
<Graphics HairColor="0.2,0.2,0.2,1" />
<HairDef DefName="Burgundy" />
<HairDef GraphicPath="Things/Pawn/Humanoid/Hairs/Burgundy" />
<HairDef HairGender="Male" />
<HairTags Tag0="Urban" />
<HairDef Label="Burgundy" />
<HairDef ShortHash="29636" />
<HairDef Selected="True" />
<Apparel>
<OnSkin Layer="OnSkin" Label="Button-down shirt" GraphicPath="Things/Pawn/Humanoid/Apparel/ShirtButton/ShirtButton" Color="0,1,0,1" />
<Shell Layer="Shell" Label="Duster" GraphicPath="Things/Pawn/Humanoid/Apparel/Duster/Duster" Color="0,1,0,1" />
</Apparel>
<Backstories>
<Childhood Index="134" />
<Adulthood Index="43" />
</Backstories>
<SkillPool Amount="40" />
<Skills Name="Construction" Value="0" Passion="1" />
<Skills Name="Growing" Value="0" Passion="2" />
<Skills Name="Research" Value="0" Passion="3" />
<Skills Name="Mining" Value="0" Passion="0" />
<Skills Name="Shooting" Value="0" Passion="0" />
<Skills Name="Melee" Value="0" Passion="0" />
<Skills Name="Social" Value="0" Passion="0" />
<Skills Name="Cooking" Value="0" Passion="0" />
<Skills Name="Medicine" Value="0" Passion="0" />
<Skills Name="Artistic" Value="0" Passion="0" />
<Skills Name="Crafting" Value="0" Passion="0" />
<End />
</Colonist>
<Colonist num="3">
<BasicInfo Name="Troy Guts Owens" />
<BasicInfo Age="63" />
<BasicInfo Gender="1" />
<BasicInfo Trait1="Poor self-esteem" />
<BasicInfo Trait2="Bad back" />
<Graphics BodyType="Male" />
<Graphics SkinColor="0.9490196,0.9294118,0.8784314,1" />
<Graphics CrownType="Average" />
<Graphics HeadGraphicPath="Things/Pawn/Humanoid/Heads/Male/Male_Average_Pointy" />
<Graphics HairColor="0.25,0.2,0.15,1" />
<HairDef DefName="Tuft" />
<HairDef GraphicPath="Things/Pawn/Humanoid/Hairs/Tuft" />
<HairDef HairGender="MaleUsually" />
<HairTags Tag0="Punk" />
<HairTags Tag1="Tribal" />
<HairDef Label="Tuft" />
<HairDef ShortHash="4402" />
<HairDef Selected="True" />
<Apparel>
<OnSkin Layer="OnSkin" Label="Button-down shirt" GraphicPath="Things/Pawn/Humanoid/Apparel/ShirtButton/ShirtButton" Color="0,0,1,1" />
<Shell Layer="Shell" Label="Duster" GraphicPath="Things/Pawn/Humanoid/Apparel/Duster/Duster" Color="0,0,1,1" />
</Apparel>
<Backstories>
<Childhood Index="196" />
<Adulthood Index="122" />
</Backstories>
<SkillPool Amount="40" />
<Skills Name="Construction" Value="0" Passion="1" />
<Skills Name="Growing" Value="0" Passion="2" />
<Skills Name="Research" Value="0" Passion="3" />
<Skills Name="Mining" Value="0" Passion="0" />
<Skills Name="Shooting" Value="0" Passion="0" />
<Skills Name="Melee" Value="0" Passion="0" />
<Skills Name="Social" Value="0" Passion="0" />
<Skills Name="Cooking" Value="0" Passion="0" />
<Skills Name="Medicine" Value="0" Passion="0" />
<Skills Name="Artistic" Value="0" Passion="0" />
<Skills Name="Crafting" Value="0" Passion="0" />
<End />
</Colonist>
</Colonists>
and I'm trying to deserialize (is that the correct term?) with this code:
private static void RecurseXmlDocument(XmlNode root, ref int num, int tags, int skill)
{
if (num < ColonistManager.Population.Count)
{
if (root is XmlElement)
{
if (root.Name == "End")
{
skill = 0;
tags = 0;
num = num + 1;
}
if (root.Attributes != null)
{
foreach (XmlAttribute attribute in root.Attributes)
{
string text = attribute.Value;
if (root.Name == "BasicInfo" && attribute.Name == "Name")
{
string name = text;
List<string> namelist = new List<string>();
namelist = name.Split().ToList<string>();
ColonistManager.Population[num].FirstName = namelist[0];
ColonistManager.Population[num].NickName = namelist[1];
ColonistManager.Population[num].LastName = namelist[2];
}
else if (root.Name == "BasicInfo" && attribute.Name == "Age")
{
int age;
string value = text;
bool result = int.TryParse(value, out age);
ColonistManager.Population[num].Age = age;
}
else if (root.Name == "BasicInfo" && attribute.Name == "Gender")
{
int gender;
string value = text;
bool result = int.TryParse(value, out gender);
ColonistManager.Population[num].Gender = gender;
}
else if (root.Name == "BasicInfo" && attribute.Name == "Trait1")
{
ColonistManager.Population[num].Traits[0].TraitName = text;
}
else if (root.Name == "BasicInfo" && attribute.Name == "Trait2")
{
ColonistManager.Population[num].Traits[1].TraitName = text;
}
else if (root.Name == "Graphics" && attribute.Name == "BodyType")
{
int bodyType = 0;
if (text == "Male")
{
bodyType = 1;
}
else if (text == "Female")
{
bodyType = 2;
}
else if (text == "Thin")
{
bodyType = 3;
}
else if (text == "Hulk")
{
bodyType = 4;
}
else if (text == "Fat")
{
bodyType = 5;
}
ColonistManager.Population[num].BodyType = (BodyType)bodyType;
}
else if (root.Name == "Graphics" && attribute.Name == "SkinColor")
{
float fcolor = 0;
List<string> Colors = new List<string>();
List<float> fColors = new List<float>();
Colors = text.Split(',').ToList<string>();
foreach (string color in Colors)
{
bool result = float.TryParse(color, out fcolor);
fColors.Add(fcolor);
}
Color skinColor = new Color();
skinColor.r = fColors[0];
skinColor.g = fColors[1];
skinColor.b = fColors[2];
skinColor.a = fColors[3];
ColonistManager.Population[num].SkinColor = skinColor;
}
else if (root.Name == "Graphics" && attribute.Name == "CrownType")
{
int crownType = 0;
if (text == "Average")
{
crownType = 1;
}
else if (text == "Narrow")
{
crownType = 2;
}
ColonistManager.Population[num].CrownType = (CrownType)crownType;
}
else if (root.Name == "Graphics" && attribute.Name == "HeadGraphicPath")
{
ColonistManager.Population[num].HeadGraphicPath = text;
}
else if (root.Name == "Graphics" && attribute.Name == "HairColor")
{
float fcolor = 0;
List<string> Colors = new List<string>();
List<float> fColors = new List<float>();
Colors = text.Split(',').ToList<string>();
foreach (string color in Colors)
{
bool result = float.TryParse(color, out fcolor);
fColors.Add(fcolor);
}
Color hairColor = new Color();
hairColor.r = fColors[0];
hairColor.g = fColors[1];
hairColor.b = fColors[2];
hairColor.a = fColors[3];
ColonistManager.Population[num].HairColor = hairColor;
}
else if (root.Name == "HairDef" && attribute.Name == "DefName")
{
ColonistManager.Population[num].HairDefItem.hairDef.defName = text;
}
else if (root.Name == "HairDef" && attribute.Name == "GraphicPath")
{
ColonistManager.Population[num].HairDefItem.hairDef.graphicPath = text;
}
else if (root.Name == "HairDef" && attribute.Name == "HairGender")
{
int hairGender = 0;
if (text == "Any")
{
hairGender = 2;
}
else if (text == "Female")
{
hairGender = 4;
}
else if (text == "FemaleUsually")
{
hairGender = 3;
}
else if (text == "MaleUsually")
{
hairGender = 1;
}
ColonistManager.Population[num].HairDefItem.hairDef.hairGender = (HairGender)hairGender;
ColonistManager.Population[num].HairDefItem.hairDef.hairTags.Clear();
}
else if (root.Name == "HairTags")
{
ColonistManager.Population[num].HairDefItem.hairDef.hairTags.Add(text);
tags = tags + 1;
}
else if (root.Name == "HairDef" && attribute.Name == "Label")
{
ColonistManager.Population[num].HairDefItem.hairDef.label = text;
}
else if (root.Name == "HairDef" && attribute.Name == "ShortHash")
{
ushort shortHash = 0;
string value = text;
bool result = ushort.TryParse(value, out shortHash);
ColonistManager.Population[num].HairDefItem.hairDef.shortHash = shortHash;
}
else if (root.Name == "HairDef" && attribute.Name == "Selected")
{
if (text == "True")
{
ColonistManager.Population[num].HairDefItem.selected = true;
}
}
else if (root.Name == "OnSkin" && attribute.Name == "Layer")
{
ColonistManager.Population[num].Clothing[0].Layer = text;
}
else if (root.Name == "OnSkin" && attribute.Name == "Label")
{
ColonistManager.Population[num].Clothing[0].Label = text;
}
else if (root.Name == "OnSkin" && attribute.Name == "GraphicPath")
{
ColonistManager.Population[num].Clothing[0].GraphicPath = text;
}
else if (root.Name == "OnSkin" && attribute.Name == "Color")
{
float fcolor = 0;
List<string> Colors = new List<string>();
List<float> fColors = new List<float>();
Colors = text.Split(',').ToList<string>();
foreach (string color in Colors)
{
bool result = float.TryParse(color, out fcolor);
fColors.Add(fcolor);
}
Color clothColor = new Color();
clothColor.r = fColors[0];
clothColor.g = fColors[1];
clothColor.b = fColors[2];
clothColor.a = fColors[3];
ColonistManager.Population[num].Clothing[0].Color = clothColor;
Log.Message("Colonist " + num.ToString() + " Shirt Color Before: " + ColonistManager.Population[num].Clothing[0].Color.r.ToString() + ", " + ColonistManager.Population[num].Clothing[0].Color.g.ToString() + ", " + ColonistManager.Population[num].Clothing[0].Color.b.ToString() + ", " + ColonistManager.Population[num].Clothing[0].Color.a.ToString());
}
else if (root.Name == "Shell" && attribute.Name == "Layer")
{
ColonistManager.Population[num].Clothing[1].Layer = text;
}
else if (root.Name == "Shell" && attribute.Name == "Label")
{
ColonistManager.Population[num].Clothing[1].Label = text;
}
else if (root.Name == "Shell" && attribute.Name == "GraphicPath")
{
ColonistManager.Population[num].Clothing[1].GraphicPath = text;
}
else if (root.Name == "Shell" && attribute.Name == "Color")
{
float fcolor = 0;
List<string> Colors = new List<string>();
List<float> fColors = new List<float>();
Colors = text.Split(',').ToList<string>();
foreach (string color in Colors)
{
bool result = float.TryParse(color, out fcolor);
fColors.Add(fcolor);
}
Color clothColor = new Color();
clothColor.r = fColors[0];
clothColor.g = fColors[1];
clothColor.b = fColors[2];
clothColor.a = fColors[3];
ColonistManager.Population[num].Clothing[1].Color = clothColor;
Log.Message("Colonist " + num.ToString() + " Coat Color Before: " + ColonistManager.Population[num].Clothing[1].Color.r.ToString() + ", " + ColonistManager.Population[num].Clothing[1].Color.g.ToString() + ", " + ColonistManager.Population[num].Clothing[1].Color.b.ToString() + ", " + ColonistManager.Population[num].Clothing[1].Color.a.ToString());
}
else if (root.Name == "Childhood" && attribute.Name == "Index")
{
int index;
string value = text;
bool result = int.TryParse(value, out index);
ColonistManager.Population[num].Backstory[0] = ColonistManager.Backstories[index];
}
else if (root.Name == "Adulthood" && attribute.Name == "Index")
{
int index;
string value = text;
bool result = int.TryParse(value, out index);
ColonistManager.Population[num].Backstory[1] = ColonistManager.Backstories[index];
}
else if (root.Name == "SkillPool" && attribute.Name == "Amount")
{
int amount;
string value = text;
bool result = int.TryParse(value, out amount);
ColonistManager.Population[num].SkillPool = amount;
}
else if (root.Name == "Skills" && attribute.Name == "Value")
{
int skillValue;
string value = text;
bool result = int.TryParse(value, out skillValue);
ColonistManager.Population[num].Skills[skill].SkillValue += skillValue;
}
else if (root.Name == "Skills" && attribute.Name == "Passion")
{
int skillPassion;
string value = text;
bool result = int.TryParse(value, out skillPassion);
ColonistManager.Population[num].Skills[skill].SkillPassion = skillPassion;
skill = skill + 1;
}
}
}
if (root.HasChildNodes)
RecurseXmlDocument(root.FirstChild, ref num, tags, skill);
if (root.NextSibling != null)
RecurseXmlDocument(root.NextSibling, ref num, tags, skill);
}
}
}
Everything imports correctly, and the Log file reads:
Colonist 0 Shirt Color Before: 1, 0, 0, 1
Colonist 0 Coat Color Before: 1, 0, 0, 1
Colonist 1 Shirt Color Before: 0, 1, 0, 1
Colonist 1 Coat Color Before: 0, 1, 0, 1
Colonist 2 Shirt Color Before: 0, 0, 1, 1
Colonist 2 Coat Color Before: 0, 0, 1, 1
but when I recheck the values with this code:
private static void VerifyLoad()
{
for (int i = 0; i < ColonistManager.Population.Count; i++)
{
Log.Message("Colonist " + i.ToString() + " Shirt Color After: " + ColonistManager.Population[i].Clothing[0].Color.r.ToString() + ", " + ColonistManager.Population[i].Clothing[0].Color.g.ToString() + ", " + ColonistManager.Population[i].Clothing[0].Color.b.ToString() + ", " + ColonistManager.Population[i].Clothing[0].Color.a.ToString());
Log.Message("Colonist " + i.ToString() + " Coat Color After: " + ColonistManager.Population[i].Clothing[1].Color.r.ToString() + ", " + ColonistManager.Population[i].Clothing[1].Color.g.ToString() + ", " + ColonistManager.Population[i].Clothing[1].Color.b.ToString() + ", " + ColonistManager.Population[i].Clothing[1].Color.a.ToString());
}
}
the Log shows:
Colonist 0 Shirt Color After: 0, 0, 1, 1
Colonist 0 Coat Color After: 1, 0, 0, 1
Colonist 1 Shirt Color After: 0, 0, 1, 1
Colonist 1 Coat Color After: 0, 0, 1, 1
Colonist 2 Shirt Color After: 0, 0, 1, 1
Colonist 2 Coat Color After: 0, 0, 1, 1
It's completely baffling me why the variables would change seeming out-of-the-blue, so any help would be greatly appreciated!
Upvotes: 0
Views: 360
Reputation: 5189
Most of the time one would simply use XmlSerializer to go from Xml to objects. In your case, the Xml is not very well suited for automatic serialization due to the irregular conventions used within like elements with the same name, but different attributes (ex: BasicInfo, Graphics elements), and attributes with multiple values (ex: all Color attributes). So manually parsing as you have have done seems like the best way to handle this particular Xml.
However, the problem you are having is in the parsing logic. If you cleaned up your parsing logic it may be easier to see the problem. One huge recursive method with a bunch if
statements like you have is going to be difficult to debug.
Consider rewriting the parsing logic using a Visitor Pattern. Your code will be much easier to maintain and modify. Below is an example of how to use the Visitor Pattern on your specific Xml. When I ran this I got the expected results.
static void Main()
{
List<Colonist> colonists = Parse();
for (int i = 0; i < colonists.Count; i++)
{
Console.WriteLine("Colonist " + i.ToString() + " Shirt Color After: " + colonists[i].Clothing[0].Color.R.ToString() + ", " + colonists[i].Clothing[0].Color.G.ToString() + ", " + colonists[i].Clothing[0].Color.B.ToString() + ", " + colonists[i].Clothing[0].Color.A.ToString());
Console.WriteLine("Colonist " + i.ToString() + " Coat Color After: " + colonists[i].Clothing[1].Color.R.ToString() + ", " + colonists[i].Clothing[1].Color.G.ToString() + ", " + colonists[i].Clothing[1].Color.B.ToString() + ", " + colonists[i].Clothing[1].Color.A.ToString());
}
}
private static List<Colonist> Parse()
{
List<Colonist> colonists = new List<Colonist>();
using (XmlTextReader reader = new XmlTextReader(File.OpenRead("XMLFile1.xml")))
{
while (reader.Read())
{
switch (reader.Name)
{
case "Colonist":
var colonist = VisitColonist(reader);
colonists.Add(colonist);
break;
}
}
}
return colonists;
}
private static Colonist VisitColonist(XmlTextReader reader)
{
Colonist colonist = new Colonist();
while (reader.Read())
{
if (reader.Name == "Colonist" && reader.NodeType == XmlNodeType.EndElement)
break;
switch(reader.Name)
{
case "BasicInfo":
VisitBasicInfo(reader, colonist);
break;
case "OnSkin":
VisitOnSkin(reader, colonist);
break;
case "Shell":
VisitShell(reader, colonist);
break;
}
}
return colonist;
}
private static void VisitBasicInfo(XmlTextReader reader, Colonist colonist)
{
while (reader.MoveToNextAttribute())
{
switch(reader.Name)
{
case "Name":
var parts = reader.Value.Split(' ');
colonist.FirstName = parts[0];
colonist.NickName = parts[1];
colonist.LastName = parts[2];
break;
case "Age":
colonist.Age = Int32.Parse(reader.Value);
break;
}
}
}
private static void VisitOnSkin(XmlTextReader reader, Colonist colonist)
{
Clothing clothing = new Clothing();
while (reader.MoveToNextAttribute())
{
switch (reader.Name)
{
case "Color":
clothing.Color = GetColor(reader.Value);
break;
}
}
colonist.Clothing[0] = clothing;
}
private static void VisitShell(XmlTextReader reader, Colonist colonist)
{
Clothing clothing = new Clothing();
while (reader.MoveToNextAttribute())
{
switch (reader.Name)
{
case "Color":
clothing.Color = GetColor(reader.Value);
break;
}
}
colonist.Clothing[1] = clothing;
}
private static Color GetColor(string colorValue)
{
var parts = colorValue.Split(',');
Color color = new Color {
R = Single.Parse(parts[0]),
G = Single.Parse(parts[1]),
B = Single.Parse(parts[2]),
A = Single.Parse(parts[3])
};
return color;
}
I just parsed a portion of the file. Here are the classes I used:
public class Colonist
{
public string FirstName { get; set; }
public string NickName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public Clothing[] Clothing { get; set; }
public Colonist()
{
Clothing = new Clothing[2];
}
}
public class Clothing
{
public Color Color { get; set; }
}
public class Color
{
public float R { get; set; }
public float G { get; set; }
public float B { get; set; }
public float A { get; set; }
}
Upvotes: 1