I'm trying to parse an XML file of size around 400KB. But I cannot overcome the stack overflow exception. First I create XmlReader and pass it to the XML file. Then I create XElement from the XmlReader.
This is my code:
private ViewContent ParseToView(XElement xElement)
{
ViewContent viewContent = new ViewContent();
viewContent.elementName = xElement.Name.LocalName;
foreach (XAttribute item in xElement.Attributes())
{
viewContent.attributes.Add(new ElementAttribute(item.Name.ToString(), item.Value));
}
foreach (XElement item in xElement.Elements())
{
viewContent.viewContents.Add(ParseToView(xElement));
}
return new ViewContent();
}
}
public class ViewContent
{
public string elementName;
public List<ElementAttribute> attributes = new List<ElementAttribute>();
public List<ViewContent> viewContents = new List<ViewContent>();
}
public class ElementAttribute
{
public ElementAttribute(string attributeName, string attributeValue)
{
this.attributeName = attributeName;
this.attributeValue = attributeValue;
}
public string attributeName;
public string attributeValue;
}
In method ParseToView you are calling same method recursively but you're calling it with same parameter - viewContent.viewContents.Add(ParseToView(xElement)); - this causes stackoverflow:
viewContent.viewContents.Add(ParseToView(xElement));
should have probably been:
viewContent.viewContents.Add(ParseToView(item));
in method:
private ViewContent ParseToView(XElement xElement)
{
ViewContent viewContent = new ViewContent();
viewContent.elementName = xElement.Name.LocalName;
foreach (XAttribute item in xElement.Attributes())
{
viewContent.attributes.Add(new ElementAttribute(item.Name.ToString(), item.Value));
}
foreach (XElement item in xElement.Elements())
{
viewContent.viewContents.Add(ParseToView(xElement)); // <-Faulty line here
}
return new ViewContent();
}
}
Related
Hello in a C# WCF Service application i want to return an array of 5 strings in a method. The above code isn't returning any errors but when i launch the Service in debug mode it only shows the first string on the Array.
Here's the IService side :
[OperationContract]
string[] NaviresXml();
Here's the Service side :
public string[] NaviresXml()
{
try
{
XMLReader x = new XMLReader(FilePath);
return new string[] { x.ReadXmlDocument_Navires() };
}
catch (Exception ex)
{
throw new Exception(ex.Message + "\n" + ex.StackTrace);
}
}
And the XMLReader Class :
public class XMLReader
{
public string XmlFilePath { get; set; }
public XMLReader(string XmlFilePath)
{
this.XmlFilePath = XmlFilePath;
}
public string ReadXmlDocument_Navires()
{
XmlDocument xmlDoc1 = new XmlDocument();
xmlDoc1.Load(XmlFilePath);
XmlNodeList itemNodes = xmlDoc1.GetElementsByTagName("Navire");
if (itemNodes.Count > 0)
{
foreach (XmlElement node in itemNodes)
return "Navire" + node.Attributes["Type"].Value + "Nom" + node.Attributes["Nom"].Value;
}
return null;
}
}
When i launch the Service i can see only the first string but not the others.
enter image description here
What is wrong with this code?
I've tried to do it without the XMLReader Class and put the code directly in the Service side but this didn't worked.
Move the return statement outside your loop.
StringBuilder stringContent = new StringBuilder();
if (itemNodes.Count > 0)
{
foreach (XmlElement node in itemNodes)
stringContent.Append("Navire" + node.Attributes["Type"].Value + "Nom" + node.Attributes["Nom"].Value);
}
return stringContent.ToString();
When a return statement is executed, function execution stops and jumps out of the executing function, even if there are other statements in the body of the function. The code after return is not executed.
So you need to put the return out of the loop like this:
public class XMLReader
{
public string XmlFilePath { get; set; }
public XMLReader(string XmlFilePath)
{
this.XmlFilePath = XmlFilePath;
}
public string ReadXmlDocument_Navires()
{
XmlDocument xmlDoc1 = new XmlDocument();
xmlDoc1.Load(XmlFilePath);
XmlNodeList itemNodes = xmlDoc1.GetElementsByTagName("Navire");
StringBuilder res = new StringBuilder();
if (itemNodes.Count > 0)
{
foreach (XmlElement node in itemNodes)
res.append("Navire" + node.Attributes["Type"].Value + "Nom" + node.Attributes["Nom"].Value);
}
return res.ToString();
}
return null;
}
I need to write an Exception class that the message is determined automatically according to the input parameters. How can I pass that dynamic message to the base()?
Here is the codes of what I want to do but it's not working
public class MyException : Exception
{
public MyException(int ErrCode, Dictionary<string, string> Params) : base(msg_external)
{
ErrorDetail = ErrorList[ErrCode];
string msg_internal = "";
string msg_external = "";
foreach (var item in Params)
{
msg_internal = ErrorDetail.MessageInternal.Replace(item.Key, item.Value);
msg_external = ErrorDetail.MessageExternal.Replace(item.Key, item.Value);
}
//now I need to pass msg_external to the base(). how?
}
}
This question is not about calling constructor after my constructor, it is about passing a different value to the parent constructor.
Here's code that'll do what you want.
Assumes good input. Error checking recommended.
public class MyException : Exception
{
private static string BaseExceptionMessage(int ErrCode, Dictionary<string,string> Params)
{
return Params.Aggregate(ErrorList[ErrCode].MessageExternal, (i, a) => i.Replace(a.Key, a.Value));
}
public MyException(int ErrCode, Dictionary<string, string> Params) : base(BaseExceptionMessage(ErrCode, Params))
{
ErrorDetail = ErrorList[ErrCode];
string msg_internal = "";
string msg_external = "";
foreach (var item in Params)
{
msg_internal = ErrorDetail.MessageInternal.Replace(item.Key, item.Value);
msg_external = ErrorDetail.MessageExternal.Replace(item.Key, item.Value);
}
}
}
I am currently developing a software that will be used by users that should not be able to access the back-end of it all but should still be able to easily change configuration/settings for the application.
I decided the best approach would be a custom "configuration file (.cfg)" located in the root of the final build.
Simple example of the .cfg file:
serveraddress='10.10.10.10'
serverport='1234'
servertimeout='15000'
Since I wanted the configuration file to easily be extended I decided to use some custom attributes and some simple LINQ.
This does work like I expect it to, but since I am still a novice in .net I am afraid I have not gone with the best approach and my question is therefor:
Is there anything I can do to improve this?
Or is there just generally a better approach for this?
This is my code for reading the configuration file and assigning the values to it's corresponding properties.
ConfigFileHandler.cs
public void ReadConfigFile()
{
var cfgFile = new ConfigFile();
var configLines = File.ReadAllLines("configfile.cfg");
var testList = configLines.Select(line => line.Split('='))
.Select(splitString => new Tuple<string, string>(splitString[0], splitString[1].Replace("'", "")))
.ToList();
foreach (var prop in typeof(ConfigFile).GetProperties())
{
var attrs = (ConfigFileFieldAttribute[])prop.GetCustomAttributes
(typeof(ConfigFileFieldAttribute), false);
foreach (var t in from attr in attrs from t in testList where t.Item1 == attr.Name select t)
{
prop.SetValue(cfgFile, t.Item2);
}
}
}
ConfigFile.cs
class ConfigFile
{
private static string _serverAddress;
private static int _serverPort;
private static int _serverTimeout;
[ConfigFileField(#"serveraddress")]
public string ServerAddress
{
get { return _serverAddress; }
set { _serverAddress= value; }
}
[ConfigFileField(#"serverport")]
public string ServerPort
{
get { return _serverPort.ToString(); }
set { _serverPort= int.Parse(value); }
}
[ConfigFileField(#"servertimeout")]
public string ServerTimeout
{
get { return _serverTimeout.ToString(); }
set { _serverTimeout= int.Parse(value); }
}
}
any tips on writing better looking code would be highly appreciated!
UPDATE:
Thanks for all the feedback.
Below is the final classes!
https://dotnetfiddle.net/bPMnJA for a live example
Please note, this is C# 6.0
ConfigFileHandler.cs
public class ConfigFileHandler
{
public void ReadConfigFile()
{
var configLines = File.ReadAllLines("configfile.cfg");
var configDictionary = configLines.Select(line => line.Split('='))
.Select(splitString => new Tuple<string, string>(splitString[0], splitString[1].Replace("'", "")))
.ToDictionary(kvp => kvp.Item1, kvp => kvp.Item2);
ConfigFile.SetDictionary(configDictionary);
}
}
ConfigFile.cs
public class ConfigFile
{
private static Dictionary<string, string> _configDictionary;
public string ServerAddress => PullValueFromConfig<string>("serveraddress", "10.1.1.10");
public int ServerPort => PullValueFromConfig<int>("serverport", "3306");
public long ServerTimeout => PullValueFromConfig<long>("servertimeout", "");
private static T PullValueFromConfig<T>(string key, string defaultValue)
{
string value;
if (_configDictionary.TryGetValue(key, out value) && value.Length > 0)
return (T) Convert.ChangeType(value, typeof (T));
return (T) Convert.ChangeType(defaultValue, typeof (T));
}
public static void SetDictionary(Dictionary<string, string> configValues)
{
_configDictionary = configValues;
}
}
You could keep the simplicity of your config file and get rid of the nested loops by loading the values into a dictionary and then passing that into your ConfigFile class.
public static void ReadConfigFile()
{
var configLines = File.ReadAllLines("configfile.cfg");
var testList = configLines.Select(line => line.Split('='))
.Select(splitString => new Tuple<string, string>(splitString[0], splitString[1].Replace("'", "")))
.ToDictionary(kvp => kvp.Item1, kvp => kvp.Item2);
var cfgFile = new ConfigFile(testList);
}
The new ConfigFile class:
class ConfigFile
{
private Dictionary<string, string> _configDictionary;
public ConfigFile(Dictionary<string, string> configValues)
{
_configDictionary = configValues;
}
public string ServerAddress
{
get { return PullValueFromConfig("serveraddress", "192.168.1.1"); }
}
public string ServerPort
{
get { return PullValueFromConfig("serverport", "80"); }
}
public string ServerTimeout
{
get { return PullValueFromConfig("servertimeout", "900"); }
}
private string PullValueFromConfig(string key, string defaultValue)
{
string value;
if (_configDictionary.TryGetValue(key, out value))
return value;
return defaultValue;
}
}
I decided to use a custom "configuration file (.cfg)" located in the root of the final build.
Good idea. For cleaner code, you could use JSON and JSON.NET for de/serialization and put the read/write into the ConfigFile class. Here is an example that is live as a fiddle.
The ConfigFile class is responsible for loading and saving itself and uses JSON.NET for de/serialization.
public class ConfigFile
{
private readonly static string path = "somePath.json";
public string ServerAddress { get; set; }
public string ServerPort { get; set; }
public string ServerTimeout { get; set; }
public void Save()
{
var json = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(path, json)
}
public static ConfigFile Load()
{
var json = File.ReadAllText(path);
return JsonConvert.DeserializeObject<ConfigFile>(json);
}
}
Here is how you would use it to load the file, change its properties, and save.
ConfigFile f = ConfigFile.Load();
f.ServerAddress = "0.0.0.0";
f.ServerPort = "8080";
f.ServerTimeout = "400";
f.Save();
We use the .json file extension as a convention. You could still use .cfg because it's just plain text with a specific syntax. The resultant config file content from the above usage is this:
{
"ServerAddress":"0.0.0.0",
"ServerPort":"8080",
"ServerTimeout":"400"
}
You could just tell your clients to "change the numbers only". Your approach is fine, as far as I'm concerned. The above is just a cleaner implementation.
Firstly, I would do what Phil did, and store your testlist in a Dictionary.
var configLines = File.ReadAllLines("configfile.cfg");
var testDict = configLines.Select(line => line.Split('=', 2))
.ToDictionary(s => s[0], s => s[1].Replace("'", ""));
Then you can clean up the property assignment LINQ a bit:
foreach (var prop in typeof(ConfigFile).GetProperties())
{
var attr = prop.GetCustomAttributes(false)
.OfType<ConfigFileFieldAttribute>()
.FirstOrDefault();
string val;
if (attr != null && testDict.TryGetValue(attr.Name, out val))
prop.SetValue(cfgFile, val);
}
You might even be able to call:
var attr = prop.GetCustomAttributes<ConfigFileFieldAttribute>(false).FirstOrDefault();
Don't have an IDE on me so I can't check right now
Is there easier way to convert telerik orm entity list to csv format?
The following simple static class will help you in this task. Note that it will create a .csv file, which contains the values of the entity's properties without taking into account the navigation properties:
public static partial class EntitiesExporter
{
public static void ExportEntityList<T>(string fileLocation, IEnumerable<T> entityList, string seperator = " , ")
{
string content = CreateFileContent<T>(entityList, seperator);
SaveContentToFile(fileLocation, content);
}
private static string CreateFileContent<T>(IEnumerable<T> entityList, string seperator)
{
StringBuilder result = new StringBuilder();
List<PropertyInfo> properties = new List<PropertyInfo>();
foreach (PropertyInfo item in typeof(T).GetProperties())
{
if (item.CanWrite)
{
properties.Add(item);
}
}
foreach (T row in entityList)
{
var values = properties.Select(p => p.GetValue(row, null));
var line = string.Join(seperator, values);
result.AppendLine(line);
}
return result.ToString();
}
private static void SaveContentToFile(string fileLocation, string content)
{
using (StreamWriter writer = File.CreateText(fileLocation))
{
writer.Write(content);
writer.Close();
}
}
}
You can consume the class like this in your code:
using (EntitiesModel dbContext = new EntitiesModel())
{
IQueryable<Category> cats = dbContext.Categories;
string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string fileLocation = Path.Combine(appDir, "test.csv");
EntitiesExporter.ExportEntityList<Category>(fileLocation, cats);
}
I hope this helps.
Is there an easy way to construct class from a XML. The constructed class will be used to serialize and deserialize XML.
I have an XML with lots of properties and elements defined. Do I need to manually create my class based on that XML? Or Is there a utility tool available to generate class from XML
Thanks,
Esen
Further on Willem's post:
This will generate the XSD (not dataset)
xsd.exe myCustom.xml
This generates the C# class:
xsd.exe myCustom.xsd /c
There's a round about way:
Using xsd.exe, you can first create a schema (xsd) from your xml file, which can then be used as input for xsd.exe to generate classes from the schema.
i.e. (from the command prompt):
xsd.exe myXmlFile.xml
to output myXmlFile.xsd
and next
xsd.exe myXmlFile.xsd
to generate classes from the xsd file.
#Willem van Rumpt: solution helped me to generate class. But in some case when I try to instantiate the dataset, I end up receiving this exception "Same Table cannot be the child table in two nested relations..."
I have tried different solution using xmldocument object to navigate each nodes and generate my class that can be used to serialize and deserialize xml file. Thought to post it here so that it would be helpful to someone who is looking for similar solution. Please post your optimized solution if you have one.
namespace Utility1
{
public static class XMLHelper
{
private enum XMLType
{
Element,
Attribute
}
public static string GenerateXMLClass(string xmlstring)
{
XmlDocument xd = new XmlDocument();
xd.LoadXml(xmlstring);
XmlNode rootNode = xd.DocumentElement;
var xmlClassCollection = new Dictionary<string, XMLClass>();
var xmlClass = new XMLClass();
xmlClassCollection.Add(rootNode.Name, xmlClass);
CollectAttributes(ref xmlClass, rootNode);
CollectElements(ref xmlClass, rootNode);
CollectChildClass(ref xmlClassCollection, rootNode);
var clsBuilder = new StringBuilder();
clsBuilder.AppendLine("[XmlRoot(\"" + rootNode.Name + "\")]");
foreach (var cls in xmlClassCollection)
{
clsBuilder.AppendLine("public class " + cls.Key);
clsBuilder.AppendLine("{");
foreach (var element in cls.Value.Elements)
{
if (XMLType.Element == element.XmlType)
clsBuilder.AppendLine("[XmlElement(\"" + element.Name + "\")]");
else
clsBuilder.AppendLine("[XmlAttribute(\"" + element.Name + "\")]");
clsBuilder.AppendLine("public " + element.Type + element.Name + "{get;set;}");
}
clsBuilder.AppendLine("}");
}
return clsBuilder.ToString();
}
private static void CollectAttributes(ref XMLClass xmlClass, XmlNode node)
{
if (null != node.Attributes)
{
foreach (XmlAttribute attr in node.Attributes)
{
if (null == xmlClass.Elements.SingleOrDefault(o => o.Name == attr.Name))
xmlClass.Elements.Add(new Element("string ", attr.Name, XMLType.Attribute));
}
}
}
private static bool IsEndElement(XmlNode node)
{
if ((null == node.Attributes || node.Attributes.Count <= 0) &&
(null == node.ChildNodes || !node.HasChildNodes || (node.ChildNodes.Count == 1 && node.ChildNodes[0].NodeType == XmlNodeType.Text)))
{
return true;
}
return false;
}
private static void CollectElements(ref XMLClass xmlClass, XmlNode node)
{
foreach (XmlNode childNode in node.ChildNodes)
{
if (null == xmlClass.Elements.SingleOrDefault(o => o.Name == childNode.Name))
{
var occurance = node.ChildNodes.Cast<XmlNode>().Where(o => o.Name == childNode.Name).Count();
var appender = " ";
if (occurance > 1)
appender = "[] ";
if(IsEndElement(childNode))
{
xmlClass.Elements.Add(new Element("string" + appender, childNode.Name, XMLType.Element));
}
else
{
xmlClass.Elements.Add(new Element(childNode.Name + appender, childNode.Name, XMLType.Element));
}
}
}
}
private static void CollectChildClass(ref Dictionary<string, XMLClass> xmlClsCollection, XmlNode node)
{
foreach (XmlNode childNode in node.ChildNodes)
{
if (!IsEndElement(childNode))
{
XMLClass xmlClass;
if (xmlClsCollection.ContainsKey(childNode.Name))
xmlClass = xmlClsCollection[childNode.Name];
else
{
xmlClass = new XMLClass();
xmlClsCollection.Add(childNode.Name, xmlClass);
}
CollectAttributes(ref xmlClass, childNode);
CollectElements(ref xmlClass, childNode);
CollectChildClass(ref xmlClsCollection, childNode);
}
}
}
private class XMLClass
{
public XMLClass()
{
Elements = new List<Element>();
}
public List<Element> Elements { get; set; }
}
private class Element
{
public Element(string type, string name, XMLType xmltype)
{
Type = type;
Name = name;
XmlType = xmltype;
}
public XMLType XmlType { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
}
}
thanks,
Esen