I'm trying to deserialize some settings from an xml file. The problematic property/underlying field is one called AlertColors. I initialize the underlying field to white, yellow, and red to make sure that a new instance of this class has a valid color setting. But when I deserialize, _colorArgb ends up with six values, first three are the initialization values and the last three are the ones that are read from the xml file. But the property AlertColors do not append to the field, but changes its elements. Why do I end up with a field with six colors?
Here's the code:
private List<int> _colorArgb = new List<int>(new int[] { Color.White.ToArgb(), Color.Yellow.ToArgb(), Color.Red.ToArgb() });
public List<int> AlertColors
{
get
{
return _colorArgb;
}
set
{
for (int i = 0; i < Math.Min(_colorArgb.Count, value.Count); i++)
{
if (_colorArgb[i] != value[i])
{
HasChanged = true;
}
}
_colorArgb = value;
}
}
public bool Deserialize(string filePath)
{
if (!File.Exists(filePath))
{
Logger.Log("Error while loading the settings. File does not exist.");
return false;
}
FileStream fileStream = null;
try
{
fileStream = new FileStream(filePath, FileMode.Open);
System.Xml.Serialization.XmlSerializerFactory xmlSerializerFactory =
new XmlSerializerFactory();
System.Xml.Serialization.XmlSerializer xmlSerializer =
xmlSerializerFactory.CreateSerializer(typeof(Settings));
Settings deserializedSettings = (Settings)xmlSerializer.Deserialize(fileStream);
GetSettings(deserializedSettings);
Logger.Log("Settings have been loaded successfully from the file " + filePath);
}
catch (IOException iOException)
{
Logger.Log("Error while loading the settings. " + iOException.Message);
return false;
}
catch (ArgumentException argumentException)
{
Logger.Log("Error while loading the settings. " + argumentException.Message);
return false;
}
catch (InvalidOperationException invalidOperationException)
{
Logger.Log("Error while loading the settings. Settings file is not supported." +
invalidOperationException.Message);
return false;
}
finally
{
if (fileStream != null)
fileStream.Close();
FilePath = filePath;
}
return true;
}
protected void GetSettings(Settings settings)
{
AlertColors = settings.AlertColors;
}
And the relevant part of the xml file that I'm deserializing:
<AlertColors>
<int>-1</int>
<int>-15</int>
<int>-65536</int>
</AlertColors>
Basically, that's just how XmlSerializer works. Unless the list is null, it never expects to try and set a value. In particular, most of the time, sub-item lists don't have a setter - they are things like:
private readonly List<Child> children = new List<Child>();
public List<Child> Children { get { return children; } }
(because most people don't want external callers to reassign the list; they just want them to change the contents).
Because of this, XmlSerializer operates basically like (over-simplifying):
var list = yourObj.SomeList;
foreach({suitable child found in the data})
list.Add({new item});
One fix is to use an array rather than a list; it always expects to assign an array back to the object, so for an array it is implemented more like (over-simplifying):
var list = new List<SomeType>();
foreach({suitable child found in the data})
list.Add({new item});
yourObj.SomeList = list.ToArray();
However, for a fixed number of values, a simpler implementation might be just:
public Foo Value1 {get;set;}
public Foo Value2 {get;set;}
public Foo Value3 {get;set;}
(if you see what I mean)
To get your desired result without changing your data types, you could use a DataContractSerializer (using System.Runtime.Serialization;) instead of the normal XmlSerializer. It doesn't call default constructors therefore you will end up with 3 colours instead of 6.
var ser = new DataContractSerializer(typeof(Settings));
var reader = new FileStream(#"c:\SettingsFile.xml", FileMode.Open);
var deserializedSettings = (Settings)ser.ReadObject(reader);
Coming a bit late to the party, but I just ran into this problem as well.
The accepted answer mentions that Arrays are assigned to every time a deserialization occurs. This was very helpful. But I needed a solution that didn't require me to change the type of the properties and rewrite a million lines of code. So I came up with this:
Using XML Serializer attributes you can 'redirect' the serializer to an Array that wraps around the original property.
[XmlIgnore]
public List<int> AlertColors { get; set; } = new List<int>() { Color.White.ToArgb(), Color.Yellow.ToArgb(), Color.Red.ToArgb() });
[XmlArray(ElementName = "AlertColors")]
public long[] Dummy
{
get
{
return AlertColors.ToArray();
}
set
{
if(value != null && value.Length > 0) AlertColors = new List<int>(value);
}
}
The Dummy property has to be public in order for the serializer to access it. For me however this was a small price to pay, leaving the original property unchanged so I didn't have to modify any additional code.
Related
I have read many posts about dictionaries and serialization etc. and they all have different ways of doing things.
Being new to coding (to an extent) I am having trouble figuring out how to get my dictionary to a list or serialized to get it saved then reloaded on play. This is Unity BTW.
So here is what I am using.
I have an application manager that is mainly just a set of BOOL values that tells my mission system if something has been completed or not. Something like "GetWeapon FALSE" (then when they pick it up it changes to TRUE which tells the mission system to move to the next objective or complete)
So I have a starting list of keys,values...these then get placed into a dictionary and the values are changed within that.
I need to be able to save those values and reload them on LOAD (default PLAY mode is newgame--as you see below it resets the dictionary and copies in the starting states). I know it can't be as difficult as I am making it, just not understanding the whole serialize thing.
Most sites are showing a dictionary like {key,value} so I am getting lost on iterating through the dictionary and capturing the pairs and saving them.
Here is partial code for the appmanager (it is a singleton):
// This holds any states you wish set at game startup
[SerializeField] private List<GameState> _startingGameStates = new List<GameState>();
// Used to store the key/values pairs in the above list in a more efficient dictionary
private Dictionary<string, string> _gameStateDictionary = new Dictionary<string, string>();
void Awake()
{
// This object must live for the entire application
DontDestroyOnLoad(gameObject);
ResetGameStates();
}
void ResetGameStates()
{
_gameStateDictionary.Clear();
// Copy starting game states into game state dictionary
for (int i = 0; i < _startingGameStates.Count; i++)
{
GameState gs = _startingGameStates[i];
_gameStateDictionary[gs.Key] = gs.Value;
}
OnStateChanged.Invoke();
}
public GameState GetStartingGameState(string key)
{
for (int i = 0; i < _startingGameStates.Count; i++)
{
if (_startingGameStates[i] != null && _startingGameStates[i].Key.Equals(key))
return _startingGameStates[i];
}
return null;
}
// Name : SetGameState
// Desc : Sets a Game State
public bool SetGameState(string key, string value)
{
if (key == null || value == null) return false;
_gameStateDictionary[key] = value;
OnStateChanged.Invoke();
return true;
}
Tried something similar to this:
Dictionary<string, string> _gameStateDictionary = new Dictionary<string, string>
{
for (int i = 0; i < _gameStateDictionary.Count; i++)
string json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.Indented);
Debug.Log(json);
{
}
However all I got was the first item in the list. (I did modify the above in a for loop) I know the above is wrong, I did other iterations to just to get the dictionary to print out in the console.
Anyway, just asking for a little code help to save and load a dictionary.
Since you want to save and load those persistent anyway, personally I would not use the Inspector and bother with Unity serialization at all but rather directly serialize/deserialize from and to json.
Simply place your default dictionary as a simple json file like e.g.
{
"Key1" : "Value1",
"Key2" : "Value2",
"Key3" : "Value3",
...
}
within the folder Assets/StreamingAssets (create it if it doesn't exists). This will be your default fallback file if none exists so far.
On runtime you will load it from Application.streamingAssetsPath.
Then when saving you will not put it there but rather to the Application.persistentDataPath
using Newtonsoft.Json;
...
[SerializeField] private string fileName = "example.json";
private string defaultStatesPath => Path.Combine(Application.streamingAssetsPath, fileName);
private string persistentStatesPath => Path.Combine(Application.persistentDataPath, fileName);
private Dictionary<string, string> _gameStateDictionary
public void Load()
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath))
{
filePath = defaultStatesPath;
}
var json = File.ReadAllText(filePath);
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
public void Save()
{
var filePath = persistentStatesPath;
if(!Directory.Exists(Application.persistentDataPath))
{
Directory.CreateDirectory(Application.persistentDataPath);
}
var json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.intended);
File.WriteAllText(filePath, json);
}
If you then still want to edit the default via the Inspector you could merge both and instead of using StreamingAssets do
[SerializeField] private GameState[] defaultStates;
public void Load()
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath))
{
LoadDefaults();
return;
}
var json = File.ReadAllText(filePath);
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
public void LoadDefaults()
{
_gameStateDictionary = defaultStates.ToDictionary(gameState => gameState.Key, gameState => gameState.Value);
}
And then btw you could do
public string GetState(string key, string fallbackValue = "")
{
if(_gameStateDictionary.TryGetValue(key, out var value))
{
return value;
}
return fallbackValue;
}
You can iterate the dictionary by using its Keys collection.
foreach (var key in myDictionary.Keys)
{
var val = myDictionary[key];
}
Populate your data structure with the key/value pairs and serialize the list of items.
var missionItems = new List<MissionItem>();
foreach (var key in myDictionary.Keys)
{
var missionItem = new MissionItem();
missionItem.Key = key;
missionItem.Value = myDictionary[key];
missionItems.Add(missionItem);
}
var json = JsonUtility.ToJson(missionItems);
Loading is straight forward, deserialize your data, then loop through all the items and add each one to the empty dicitionary.
var missionItems = JsonUtility.FromJson<List<MissionItem>>(json);
foreach (var item in missionItems)
{
myDictionary.Add(item.Key, item.Value);
}
Here is my final setup.
I call these on button press in another script (the UI pause menu save/load buttons)
private bool useEncryption = true;
private string encryptionCodeWord = "CodeWordHere";
private static string fileName = "name.json";
private static string defaultStatesPath => Path.Combine(Application.streamingAssetsPath, fileName);
private static string persistentStatesPath => Path.Combine(Application.persistentDataPath, fileName);
public void SaveStates(int slotIndex)
{
var filePath = persistentStatesPath;
if(!Directory.Exists(Application.persistentDataPath))
{
Directory.CreateDirectory(Application.persistentDataPath);
}
var json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.Indented);
if (useEncryption)
{
Debug.Log("Using Encryption");
json = EncryptDecrypt(json);
}
File.WriteAllText(filePath + slotIndex, json);
}
public void LoadStates(int SlotIndex)
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath + SlotIndex))
{
filePath = defaultStatesPath;
}
var json = File.ReadAllText(filePath + SlotIndex);
if (useEncryption)
{
Debug.Log("Using Encryption");
json = EncryptDecrypt(json);
}
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
private string EncryptDecrypt(string data)
{
string modifiedData = "";
for (int i = 0; i < data.Length; i++)
{
modifiedData += (char)(data[i] ^ encryptionCodeWord[i % encryptionCodeWord.Length]);
}
return modifiedData;
}
I have this class, which takes the selected file in the grid
[HttpPost]
public ActionResult ExportXml(string apontamentos)
{
try
{
string[] arrApontamentos = apontamentos.Split(';');
var listApontamentoId = arrApontamentos.Select(x => new Guid(x)).ToList();
var apontamentosViewModel = this._apontamentoAppService.ObterTodos(listApontamentoId);
List<ApontamentoExportarViewModel> listXml = new List<ApontamentoExportarViewModel>();
int item = 1;
foreach (var informacaoApontamentoVM in apontamentosViewModel)
{
listXml.Add(new ApontamentoExportarViewModel
{
Item = item,
Equipamento = informacaoApontamentoVM.Barco.SapId,
Atendimento = informacaoApontamentoVM.Atendimento,
Escala = informacaoApontamentoVM.LocalDaOperacao.Abreviacao,
DescricaoDaOperacao = informacaoApontamentoVM.CodigosDeOperacao.Descricao,
//GrupoDeCodigo = "xxx",
CodigoOperacao = informacaoApontamentoVM.CodigosDeOperacao.Codigo,
DataInicial = string.Format("{0:dd.MM.yyyy}", informacaoApontamentoVM.DataInicio),
HoraInicial = string.Format("{0:HH.mm.ss}", informacaoApontamentoVM.DataInicio),
DataFinal = string.Format("{0:dd:MM:yyyy}", informacaoApontamentoVM.DataTermino),
HoraFinal = string.Format("{0:HH:mm:ss}", informacaoApontamentoVM.DataTermino),
Observacoes = informacaoApontamentoVM.Observacao
});
item++;
}
var status = this._apontamentoAppService.ObterDescricaoStatusApontamento(Domain.Apontamentos.StatusApontamento.Exportado);
this._apontamentoAppService.AtualizarStatus(apontamentosViewModel.Select(x => x.Id).ToList(), status);
return new XmlActionResult<ApontamentoExportarViewModel>(listXml);
}
catch (Exception ex)
{
throw ex;
}
}
And a i have that other one, which does export and xml format and filename
public class XmlActionResult<T> : ActionResult
{
public XmlActionResult(List<T> data)
{
Data = data;
}
public List<T> Data { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "text/xml";
// TODO: Use your preferred xml serializer
// to serialize the model to the response stream :
// context.HttpContext.Response.OutputStream
ApontamentoExportarViewModel apontamentoExportarViewModel = new ApontamentoExportarViewModel();
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = string.Format("MA_"+ "Equipamento" + "_{0:dd-MM-yyyy_HH-mm-ss}.xml", DateTime.Now),
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
var root = new XmlRootAttribute("meadinkent");
XmlSerializer x = new XmlSerializer(Data.GetType(), root);
context.HttpContext.Response.AppendHeader("Content-Disposition", cd.ToString());
x.Serialize(context.HttpContext.Response.OutputStream, Data);
}
}
}
Basically, I need to get the "Equipamento" attribute and insert it into the file name.
The information from the "ApontamentoExportarViewModel" class is coming from the "Data" attribute, but how do you find the information inside that list? Remembering that I only need the information of the attribute "Equipment"
How would I bring the value of this attribute to the XmlActionResult class?
Well, you've got it in Data in the XmlActionResult, but because it's a List<T>, it could be anything. In this case, it's your ApontamentoExportarViewModel view model.
Should this XmlActionResult method be able to work with various kinds of objects, or only with ApontamentoExportarViewModel? If with various types, then not every one of those types will have a Equipamento property. In that case, you would have to do something like:
var fileName = "default";
if(Data is List<ApontamentoExportarViewModel>)
{
var record = (Data as List<ApontamentoExportarViewModel>).FirstOrDefault(); // what should happen if there's more than one?
if (record != null)
fileName = record.Equipamento;
}
and then later:
FileName = string.Format("MA_"+ fileName + "_{0:dd-MM-yyyy_HH-mm-ss}.xml", DateTime.Now);
Or something like that.
If you are sure that every object that comes in to your method will have a Equipamento property, then you could create a base class from which you derive your view model classes that has Equipamento, or an interface, and then alter your method to not accept any type (<T>), but rather only classes that derive from the base class / implement the interface.
i'm a complete beginner with protobuf-net so probably this is just some stupid beginner mistake. But i cant find whats the problem with this:
I have a class thats to be serialized to disk defined like this:
[ProtoContract]
public class SerializableFactors
{
[ProtoMember(1)]
public double?[] CaF {get;set;}
[ProtoMember(2)]
public byte?[] CoF { get; set; }
}
and a Test defined like this:
if (File.Exists("factors.bin"))
{
using (FileStream file = File.OpenRead("factors.bin"))
{
_factors = Serializer.Deserialize<SerializableFactors>(file);
}
}
else
{
_factors = new SerializableFactors();
_factors.CaF = new double?[24];
_factors.CaF[8] = 7.5;
_factors.CaF[12] = 1;
_factors.CaF[18] = 1.5;
_factors.CoF = new byte?[24];
_factors.CoF[8] = 15;
_factors.CoF[12] = 45;
_factors.CoF[18] = 25;
using (FileStream file = File.Create("factors.bin"))
{
Serializer.Serialize(file, _factors);
}
}
So basically if the file doesnt exist yet i create a object with default values and serialize it to disk. If the file exists i will load it into memory.
BUT the result for me on loading the file is not what i created before the saving to disk. I create arrays with length 24 which have values in the slots 8, 12 and 18. But the deserialized object has arrays of the length 3 which hold my values.
What is my mistake in here?
Thanks in advance!
You must set the RuntimeTypeModel to support null
See the following post:
How can I persist an array of a nullable value in Protobuf-Net?
// configure the model; SupportNull is not currently available
// on the attributes, so need to tweak the model a little
RuntimeTypeModel.Default.Add(typeof(SerializableFactors), true)[1].SupportNull = true;
if (File.Exists("factors.bin"))
{
using (FileStream file = File.OpenRead("factors.bin"))
{
_factors = Serializer.Deserialize<SerializableFactors>(file);
}
}
else
{
_factors = new SerializableFactors();
_factors.CaF = new double?[24];
_factors.CaF[8] = 7.5;
_factors.CaF[12] = 1;
_factors.CaF[18] = 1.5;
_factors.CoF = new byte?[24];
_factors.CoF[8] = 15;
_factors.CoF[12] = 45;
_factors.CoF[18] = 25;
using (FileStream file = File.Create("factors.bin"))
{
Serializer.Serialize(file, _factors);
}
}
i have the following code which checks if some folders were created; The list of folders with full path is stored in an xml file.
namespace InstallationCheck
{
public class Checking
{
public bool result()
{
bool returns = true;
//Reads from xml file the element content from a tag line (ex: esecpath)
string esecpath = Checking.CitXml("C:\\testconfig.xml", "esecpath");
string agentpath = Checking.CitXml("C:\\testconfig.xml", "agentpath");
string datapath = Checking.CitXml("C:\\testconfig.xml", "datapath");
string debugpath = Checking.CitXml("C:\\testconfig.xml", "debugpath");
string helppath = Checking.CitXml("C:\\testconfig.xml", "helppath");
string patchpath = Checking.CitXml("C:\\testconfig.xml", "patchpath");
// Compare the paths from XML with the paths of the app.
List<bool> listtest = new List<bool>();
listtest.Add((Directory.Exists(esecpath) == true));
listtest.Add((Directory.Exists(agentpath) == true));
listtest.Add((Directory.Exists(datapath) == true));
listtest.Add((Directory.Exists(debugpath) == true));
listtest.Add((Directory.Exists(patchpath) == true));
listtest.Add((Directory.Exists(helppath) == true));
//Cheking if any of paths are false
foreach (bool varia in listtest)
{
returns = returns && varia;
}
return returns;
}
// Reading from XML method
public static string CitXml(string xmlpath, string field)
{
XmlTextReader xmlReader = new XmlTextReader(xmlpath);
xmlReader.WhitespaceHandling = WhitespaceHandling.None;
xmlReader.ReadToDescendant(field);
return xmlReader.ReadElementString(field);
}
}
}
Now i need to check if some files were created (many of them), to spare me of manualy adding all in to the code, i wonder how should i do it; i want the code to read the xml file and check if all of the files (from the xml) were created. So i wonder if someone of you can give an ideea, a hint (so i can go read about it), maybe a code example. Thank you.
Well, to see if any were created, re-write the code to be something like:
public bool result()
{
Dictionary<string, string> files = new Dictionary<string, string>();
files.Add("esecpath", "C:\\testconfig.xml");
// ... etc for each file
// if you want to see if any don't exist, then use ...
// if(files.Any(f => !File.Exists(f.Value)))
// else, these are all the created files
var createdFiles = files.Where(f => !File.Exists(f.Value));
if(createdFiles.Count() > 0)
{
// A file doesn't exist! Therefore you are creating it!
}
var directories = files.Select(f => Checking.CitXml(f.Value, f.Key));
return directories.All(d => Directory.Exists(d));
}
I seem to be having a problem with retrieving XML values with C#, which I know it is due to my very limited knowledge of C# and .XML.
I was given the following XML file
<PowerBuilderRunTimes>
<PowerBuilderRunTime>
<Version>12</Version>
<Files>
<File>EasySoap110.dll</File>
<File>exPat110.dll</File>
<File>pbacc110.dll</File>
</File>
</PowerBuilderRunTime>
</PowerBuilderRunTimes>
I am to process the XML file and make sure that each of the files in the exist in the folder (that's the easy part). It's the processing of the XML file that I have having a hard time with. Here is what I have done thus far:
var runtimeXml = File.ReadAllText(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes));
var doc = XDocument.Parse(runtimeXml);
var topElement = doc.Element("PowerBuilderRunTimes");
var elements = topElement.Elements("PowerBuilderRunTime");
foreach (XElement section in elements)
{
//pbVersion is grabbed earlier. It is the version of PowerBuilder
if( section.Element("Version").Value.Equals(string.Format("{0}", pbVersion ) ) )
{
var files = section.Elements("Files");
var fileList = new List<string>();
foreach (XElement area in files)
{
fileList.Add(area.Element("File").Value);
}
}
}
My issue is that the String List is only ever populated with one value, "EasySoap110.dll", and everything else is ignored. Can someone please help me, as I am at a loss.
Look at this bit:
var files = section.Elements("Files");
var fileList = new List<string>();
foreach (XElement area in files)
{
fileList.Add(area.Element("File").Value);
}
You're iterating over each Files element, and then finding the first File element within it. There's only one Files element - you need to be iterating over the File elements within that.
However, there are definitely better ways of doing this. For example:
var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes));
var fileList = (from runtime in doc.Root.Elements("PowerBuilderRunTime")
where (int) runtime.Element("Version") == pbVersion
from file in runtime.Element("Files").Elements("File")
select file.Value)
.ToList();
Note that if there are multiple matching PowerBuilderRunTime elements, that will create a list with all the files of all those elements. That may not be what you want. For example, you might want:
var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes));
var runtime = doc.Root
.Elements("PowerBuilderRunTime")
.Where(r => (int) r.Element("Version") == pbVersion)
.Single();
var fileList = runtime.Element("Files")
.Elements("File")
.Select(x => x.Value)
.ToList();
That will validate that there's exactly one matching runtime.
The problem is, there's only one element in your XML, with multiple children. You foreach loop only executes once, for the single element, not for its children.
Do something like this:
var fileSet = files.Elements("File");
foreach (var file in fileSet) {
fileList.Add(file.Value);
}
which loops over all children elements.
I always preferred using readers for reading homegrown XML config files. If you're only doing this once it's probably over kill, but readers are faster and cheaper.
public static class PowerBuilderConfigParser
{
public static IList<PowerBuilderConfig> ReadConfigFile(String path)
{
IList<PowerBuilderConfig> configs = new List<PowerBuilderConfig>();
using (FileStream stream = new FileStream(path, FileMode.Open))
{
XmlReader reader = XmlReader.Create(stream);
reader.ReadToDescendant("PowerBuilderRunTime");
do
{
PowerBuilderConfig config = new PowerBuilderConfig();
ReadVersionNumber(config, reader);
ReadFiles(config, reader);
configs.Add(config);
reader.ReadToNextSibling("PowerBuilderRunTime");
} while (reader.ReadToNextSibling("PowerBuilderRunTime"));
}
return configs;
}
private static void ReadVersionNumber(PowerBuilderConfig config, XmlReader reader)
{
reader.ReadToDescendant("Version");
string version = reader.ReadString();
Int32 versionNumber;
if (Int32.TryParse(version, out versionNumber))
{
config.Version = versionNumber;
}
}
private static void ReadFiles(PowerBuilderConfig config, XmlReader reader)
{
reader.ReadToNextSibling("Files");
reader.ReadToDescendant("File");
do
{
string file = reader.ReadString();
if (!string.IsNullOrEmpty(file))
{
config.AddConfigFile(file);
}
} while (reader.ReadToNextSibling("File"));
}
}
public class PowerBuilderConfig
{
private Int32 _version;
private readonly IList<String> _files;
public PowerBuilderConfig()
{
_files = new List<string>();
}
public Int32 Version
{
get { return _version; }
set { _version = value; }
}
public ReadOnlyCollection<String> Files
{
get { return new ReadOnlyCollection<String>(_files); }
}
public void AddConfigFile(String fileName)
{
_files.Add(fileName);
}
}
Another way is to use a XmlSerializer.
[Serializable]
[XmlRoot]
public class PowerBuilderRunTime
{
[XmlElement]
public string Version {get;set;}
[XmlArrayItem("File")]
public string[] Files {get;set;}
public static PowerBuilderRunTime[] Load(string fileName)
{
PowerBuilderRunTime[] runtimes;
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var reader = new XmlTextReader(fs);
runtimes = (PowerBuilderRunTime[])new XmlSerializer(typeof(PowerBuilderRunTime[])).Deserialize(reader);
}
return runtimes;
}
}
You can get all the runtimes strongly typed, and use each PowerBuilderRunTime's Files property to loop through all the string file names.
var runtimes = PowerBuilderRunTime.Load(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes));
You should try replacing this stuff with a simple XPath query.
string configPath;
System.Xml.XPath.XPathDocument xpd = new System.Xml.XPath.XPathDocument(cofigPath);
System.Xml.XPath.XPathNavigator xpn = xpd.CreateNavigator();
System.Xml.XPath.XPathExpression exp = xpn.Compile(#"/PowerBuilderRunTimes/PwerBuilderRunTime/Files//File");
System.Xml.XPath.XPathNodeIterator iterator = xpn.Select(exp);
while (iterator.MoveNext())
{
System.Xml.XPath.XPathNavigator nav2 = iterator.Current.Clone();
//access value with nav2.value
}