Refactoring a somehow generic method - c#

I am using a certain method of mine over and over again. Therefore, I tend to import this class to all of my projects, where I have to access the IsolatedStorage of a Windows Phone-Device.
But, it doesn't really seem elegant to me.
Take a look:
public static Object getFileContent (String filename, String returntype)
{
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
Object returnobj = new Object();
List<String> list = new List<String>();
String r;
IsolatedStorageFileStream fileStream = isf.OpenFile(filename, FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fileStream);
if (returntype.Equals("list"))
{
while ((r = reader.ReadLine()) != null)
{
list.Add(r);
}
returnobj = list;
}
else if (returntype.Equals("string"))
{
r = reader.ReadLine();
returnobj = r;
}
fileStream.Close();
reader.Close();
return returnobj;
}
My main issue is the paramter returntype. It's a obviously a string.
I'd like to have it as the type directly. But that didn't work on my last attempt.
Also, when I use this method, like for instance:
string random = (string) MyFavClass.getFileContent("randomFile","string");
I have to convert the return type of that method once again, before I can use it.

Just write two methods: getFileContentAsString and getFileContentAsList. You're not getting any benefit of genericness here if you can only support exactly two types.

You could use the strategy pattern if you're going to have an arbitrary amount of return types.
public static T GetFileContents<T>(String filename, Func<StreamReader, T> readMethod)
{
// initialize stuff
using (StreamReader reader = new StreamReader(...))
{
return readMethod(reader);
}
}
public static string ReadStreamAsString(StreamReader reader)
{
return reader.ReadLine();
}
public static List<string> ReadStreamAsList(StreamReader reader)
{
var list = new List<string>();
while ((r = reader.ReadLine()) != null)
{
list.Add(r);
}
return list;
}
And to use it:
string myString = GetFileContents("foo.txt", ReadStreamAsString);
List<string> myList = GetFileContents("bar.xml", ReadStreamAsList);
My generics are probably off, but hopefully it conveys the right idea.

Untested, but I would try to do something like this:
public static T getFileContent (String filename, Func<StreamReader, T> process)
{
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
Object returnobj = new Object();
List<String> list = new List<String>();
String r;
IsolatedStorageFileStream fileStream = isf.OpenFile(filename, FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fileStream);
try{
return process(reader);
}
finally{
fileStream.Close();
reader.Close();
}
}
I'm sure this is error-prone as I have it, but the idea is to pass in a converter function that will handle whatever type you want to return.

Assuming:
Readonly, forward only acces is all that's required from the List; and
It is desired to treat the results of both possibilities identically in the application code.
Then he following would work:
public abstract class MySequence : IEnumerable<string> {
protected string Filename { get; private set; }
public MySequence(string filename) {
Filename = filename;
}
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
public IEnumerator<string> GetEnumerator() {
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
using (var stream = isf.OpenFile(Filename, FileMode.Open, FileAccess.Read))
using (var reader = new StreamReader(stream)) {
return GetEnumerator(reader);
}
}
protected abstract IEnumerator<string> GetEnumerator(StreamReader reader);
}
public class MyListSequence : MySequence, IEnumerable<string> {
public MyListSequence(string filename) : base(filename) {}
protected override IEnumerator<string> GetEnumerator(StreamReader reader) {
while (! reader.EndOfStream) yield return reader.ReadLine();
}
}
public class MyStringSequence : MySequence, IEnumerable<string> {
public MyStringSequence(string filename) : base(filename) {}
protected override IEnumerator<string> GetEnumerator(StreamReader reader) {
yield return reader.ReadLine();
}
}
Usage would be like this:
foreach (var s in (new MyListSequence("fred"))) { Console.WriteLine(s); }
foreach (var s in (new MyStringSequence("fred"))) { Console.WriteLine(s); }
A particular advantage of this mechanism, when ti applies,is that no storage needs to be allocated for the List; eaxh text line is simply read and returned by the iterator.

Related

Accessing a Json array as string array

I have this code which reads from my json file an array of words
public static string[] GetProfanity()
{
var json = string.Empty;
using (var fs = File.OpenRead("profanity.json"))
using (var sr = new StreamReader(fs, new UTF8Encoding(false)))
json = sr.ReadToEnd();
var profanityJson = JsonConvert.DeserializeObject<ProfanityJson>(json);
return profanityJson.badwords;
}
This is the json
{
"badwords" : ["bad", "stupid"]
}
And i try to access this here
public static bool ProfanityCheck(string inputString)
{
string[] badWords = GetProfanity();
string checkString = inputString.ToLower();
if (badWords.Any(checkString.Contains))
return true;
return false;
}
As requested I access the ProfanityCheck method here
[Command("echo")]
[Description("says whatever the user gives")]
public async Task Echo(CommandContext ctx, [RemainingText] string echoText)
{
bool hasProfanity = ProfanityFilter.ProfanityCheck(echoText);
if(hasProfanity)
{
var errMsg = ProfanityFilter.ErrorMessage();
var errSent = await ctx.Channel.SendMessageAsync(embed: errMsg).ConfigureAwait(false);
Thread.Sleep(3000);
await ctx.Channel.DeleteMessageAsync(errSent).ConfigureAwait(false);
await ctx.Channel.DeleteMessageAsync(ctx.Message).ConfigureAwait(false);
return;
}
await ctx.Channel.SendMessageAsync(echoText).ConfigureAwait(false);
}
and the struct I Deserialize it as
public struct ProfanityJson
{
[JsonProperty("badwords")]
public string[] badwords { get; private set; }
}
but when i attempt to search for this any bad words in a string I pass, nothing happens, no errors in the console, no output otherwise. I have it set up so that it sends me an error message when profanity is found, but in its current state it does nothing when profanity is passed
Your code seems to be correct... I would write the GetProfanity() in another way (and I wouldn't surely reread it every time a word is passed to to ProfanityCheck) but this is tangential to your problem. I've written a minimum testable example:
public class ProfanityJson
{
public string[] badwords { get; set; }
}
public static class ProfanityChecker
{
public static string[] GetProfanity()
{
var json = string.Empty;
using (var fs = File.OpenRead("profanity.json"))
using (var sr = new StreamReader(fs, new UTF8Encoding(false)))
json = sr.ReadToEnd();
var profanityJson = JsonConvert.DeserializeObject<ProfanityJson>(json);
return profanityJson.badwords;
}
public static string[] GetProfanity2()
{
using (var sr = new StreamReader("profanity.json"))
using (var jtr = new JsonTextReader(sr))
{
var ser = new JsonSerializer();
var profanityJson = ser.Deserialize<ProfanityJson>(jtr);
return profanityJson.badwords;
}
}
public static bool ProfanityCheck(string inputString)
{
string[] badWords = GetProfanity2();
Trace.WriteLine($"Loaded {badWords.Length} bad words");
string checkString = inputString.ToLower();
if (badWords.Any(checkString.Contains))
return true;
return false;
}
}
static void Main(string[] args)
{
Console.WriteLine(ProfanityChecker.ProfanityCheck("badder"));
}
So the only idea I have is that you are using a "stale" version of profanity.json. I've added a little loggin in the ProfanityCheck() method. It will go to the Output pane in Visual Studio.
(Would be a mess as a comment)
You could have your class like this:
public class ProfanityJson
{
[JsonProperty("badwords")]
public string[] Badwords { get; set; }
}
Is it like so? Json is case sensitive.

Can't cast objects from Dictionary after using json.net deserialization

I have a problem with deserialized Dictionary. I can't cast objects from Dictionary to my type Remiza... I'm using Json.net and what I can see is that objects in Dictionary are JObject not Object and I can't cast them to my type. Here is my serialization/deserialization code:
private static Dictionary<Type, List<Object>> _ekstensje = new Dictionary<Type, List<Object>>();
public static void SerializeDictionary()
{
string json = JsonConvert.SerializeObject(_ekstensje);
System.IO.StreamWriter file = new System.IO.StreamWriter(#"c:\tmp\dictionary.json");
file.WriteLine(json);
file.Close();
}
public static void DeserializeDictionary()
{
string json;
System.IO.StreamReader file = new System.IO.StreamReader(#"c:\tmp\dictionary.json");
json = file.ReadToEnd();
file.Close();
_ekstensje = JsonConvert.DeserializeObject<Dictionary<Type, List<Object>>>(json);//Deserializacja Dictionary
Debug.WriteLine(_ekstensje);
}
public static List<Object> GetEkstensja(Type className)
{
List<Object> list = _ekstensje[className];
return list;
}
Exectution:
ObjectPlus.DeserializeDictionary();
List<Object> list = ObjectPlus.GetEkstensja(typeof(Remiza));
foreach (Object o in list)
{
Remiza r = (Remiza) o;
listaRemiz.Add(r);
}
My problem is when casting to "Remiza" I have that exception:
An exception of type 'System.InvalidCastException' occurred in Osek_MAS_WPF.exe but was not handled in user code. Additional information: Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'Osek_MAS_WPF.Remiza'.
Thanks for any help!
This should allow you to convert the JObect to your Remiza type.
ObjectPlus.DeserializeDictionary();
List<Object> list = ObjectPlus.GetEkstensja(typeof(Remiza));
foreach (Object o in list)
{
Remiza r = o.ToObject<Remiza>();
listaRemiz.Add(r);
}
I got this from the stackoverflow answer at the link below. If what I put doesn't work take a look at the link and it should help you to get it running.
https://stackoverflow.com/a/10221594/634769
In order to successfully serialize and deserialize polymorphic types with Json.NET, you need to set TypeNameHandling = TypeNameHandling.Auto, like so:
public class ObjectPlus
{
// Replace with whatever file name is appropriate. My computer doesn't have a "c:\tmp" directory.
static string JsonFileName { get { return Path.Combine(Path.GetTempPath(), "dictionary.json"); } }
static JsonSerializerSettings JsonSettings { get { return new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented }; } }
private static Dictionary<Type, List<Object>> _ekstensje = new Dictionary<Type, List<Object>>();
public static void SerializeDictionary()
{
var path = JsonFileName;
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
using (var writer = new StreamWriter(stream))
{
var serializer = JsonSerializer.CreateDefault(JsonSettings);
serializer.Serialize(writer, _ekstensje);
}
}
public static void DeserializeDictionary()
{
var path = JsonFileName;
try
{
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var reader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(reader))
{
var serializer = JsonSerializer.CreateDefault(JsonSettings);
_ekstensje = serializer.Deserialize<Dictionary<Type, List<Object>>>(jsonReader);
}
}
catch (FileNotFoundException)
{
// File was not created yet, dictionary should be empty.
_ekstensje.Clear();
}
}
public static List<Object> GetEkstensja(Type className)
{
List<Object> list = _ekstensje[className];
return list;
}
public static void AddEkstensja<T>(T obj)
{
List<Object> list;
if (!_ekstensje.TryGetValue(obj.GetType(), out list))
list = _ekstensje[obj.GetType()] = new List<object>();
list.Add(obj);
}
internal static string ShowJsonContents()
{
if (!File.Exists(JsonFileName))
return string.Empty;
return File.ReadAllText(JsonFileName);
}
}
You should now be able to serialize and deserialize your dictionary when it contains an instance of Remiza.
This will work for types that serialize to objects or collections. However, if your dictionary contains types that serialize to JSON primitives -- for instance an enum or a long -- you may need to encapsulate them in a type wrapper along the lines of Deserialize specific enum into system.enum in Json.Net.
(Incidentally, your _ekstensje dictionary isn't thread-safe.)

Deserialize an XML file and copy the values into csv

i want to deserialize an xml file and copy those values into csv file.i tried like this.
[Serializable, XmlRoot("Configuration"), XmlType("Configuration")]
public class LabelRequest
{
public string weightoz { get; set; }
public string MailClass { get; set; }
public static void DeSerialization()
{
LabelRequest label = new LabelRequest();
TextReader txtReader = new StreamReader(#"C:\xmlfile.xml");
XmlSerializer xmlSerializer = new XmlSerializer(typeof(LabelRequest));
label = (LabelRequest) xmlSerializer.Deserialize(txtReader);
txtReader.Close();
}
}
and the xml file is as follows
<Labelrequest>
<weightoz>2</weightoz>
<mailclass>abc</mailclass>
</labelrequest>
To write the values to a CSV file shouldn't be too difficult. Your example does not contain any code which writes to a file though. It only deserialises an XML file. Might I suggest something like this.
public static class LabelRequestSerializer
{
public static Label DeserializeXmlFile(string fileName)
{
using (TextReader txtReader = new StreamReader(fileName))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(LabelRequest));
LabelRequest label = (LabelRequest) xmlSerializer.Deserialize(txtReader);
}
}
public static void SerializeToCsv(LabelRequest labelRequest, string fileName)
{
if (labelRequest == null)
throw new ArgumentNullException("labelRequest");
StringBuilder sb = new StringBuilder();
sb.Append(labelRequest.weightoz);
sb.Append(",");
sb.Append(labelRequest.mailclass);
sb.AppendLine();
using (StreamWriter stream = new StreamWriter(fileName))
{
stream.Write(sb.ToString());
}
}
}
Then you can pass the instance of LabelRequest you want to serialize to these static methods. That way LabelRequest does not know about how to serialize itself from files, which is a nice seperation of concerns. Like this
void SomeMethod()
{
LabelRequest labelRequest = new LabelRequest();
LabelRequestSerializer.SerializeToCsv(labelRequest, #"C:\Path\Goes\Here\label.csv");
}
Edit...
If you really don't want to write out every property manually you can use reflection. However there will be a performance hit by using this. Shouldn't be a problem compared to the file IO though.
public static void SerializeToCsv(LabelRequest labelRequest, string fileName)
{
if (labelRequest == null)
throw new ArgumentNullException("labelRequest");
StringBuilder sb = new StringBuilder();
foreach (PropertyInfo info in labelRequest.GetType() .GetProperties())
{
object value = info.GetValue(labelRequest, null);
sb.Append(value);
sb.Append(", ");
}
sb.AppendLine();
using (StreamWriter stream = new StreamWriter(fileName))
{
stream.Write(sb.ToString());
}
}

Serialize/Deserilize array of objects/structs

I have read many codes on this but none happened to solve the problem. first the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Serialization
{
class Program
{
static void Main(string[] args)
{
using (MoveSaver objSaver = new MoveSaver(#"C:\1.bin"))
{
MoveAndTime mv1, mv2;
mv1.MoveStruc = "1";
mv1.timeHLd = DateTime.Now;
objSaver.SaveToFile(mv1);
mv2.MoveStruc = "2";
mv2.timeHLd = DateTime.Now;
objSaver.SaveToFile(mv2);
}
using (MoveSaver svrObj = new MoveSaver())
{
MoveAndTime[] MVTobjs = svrObj.DeSerializeObject(#"C:\1.bin");
foreach (MoveAndTime item in MVTobjs)
{
//Do Something
}
}
}
}
public class MoveSaver:IDisposable
{
public void Dispose()
{
if (fs != null)
{
fs.Close();
}
}
FileStream fs;
StreamWriter sw;
public string filename { get; set; }
public MoveSaver(string FileName)
{
this.filename = FileName;
fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite);
}
public MoveSaver()
{
}
~MoveSaver()
{
if (fs != null)
{
fs.Close();
}
}
public MoveAndTime[] DeSerializeObject(string filename)
{
MoveAndTime[] objectToSerialize;
Stream stream = File.Open(filename, FileMode.Open);
BinaryFormatter bFormatter = new BinaryFormatter();
objectToSerialize = (MoveAndTime[])bFormatter.Deserialize(stream);
stream.Close();
return objectToSerialize;
}
public bool SaveToFile(MoveAndTime moveTime)
{
try
{
BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Serialize(fs, moveTime);
return true;
}
catch (Exception)
{
return false;
}
}
}
[Serializable]
public struct MoveAndTime
{
public string MoveStruc;
public DateTime timeHLd;
}
}
The code mimics a need for saving all actions of user on the program. to be later shown on that program (say you play cards or so and you want to review :D what has happened). The problem is when DeSerializeObject function is called the line objectToSerialize = (MoveAndTime[])bFormatter.Deserialize(stream); throws an exception (definitely in runtime) that the cast from a single object to array is not valid:
Unable to cast object of type
'Serialization.MoveAndTime' to type
'Serialization.MoveAndTime[]'.
Any idea? Any improvement or total change of approach is appreciated.
You're saving a single MoveAndTime instance to the file, but you're trying to read an array of them.
Please modify your main block like this. I think it achieves what you want.
static void Main(string[] args)
{
using (MoveSaver objSaver = new MoveSaver(#"C:\1.bin"))
{
MoveAndTime[] MVobjects = new MoveAndTime[2];
MoveAndTime mv1, mv2;
mv2 = new MoveAndTime();
mv1 = new MoveAndTime();
mv1.MoveStruc = "1";
mv1.timeHLd = DateTime.Now;
mv2.MoveStruc = "2";
mv2.timeHLd = DateTime.Now;
MVobjects[0] = new MoveAndTime();
MVobjects[0] = mv1;
MVobjects[1] = new MoveAndTime();
MVobjects[1] = mv2;
objSaver.SaveToFile(MVobjects);
}
using (MoveSaver svrObj = new MoveSaver())
{
MoveAndTime[] MVTobjs = svrObj.DeSerializeObject(#"C:\1.bin");
foreach (MoveAndTime item in MVTobjs)
{
//Do Something
Console.WriteLine(item.MoveStruc);
Console.WriteLine(item.timeHLd);
}
}
}
Thanks

C# - Deserialize a List<String>

I can serialize a list really easy:
List<String> fieldsToNotCopy =new List<String> {"Iteration Path","Iteration ID"};
fieldsToNotCopy.SerializeObject("FieldsToNotMove.xml");
Now I need a method like this:
List<String> loadedList = new List<String();
loadedList.DeserializeObject("FieldsToNotMove.xml");
Is there such a method? Or am I going to need to create an XML reader and load it in that way?
EDIT: Turns out there is no built in SerialzeObject. I had made one earlier in my project and forgot about it. When I found it I thought it was built in. In case you are curious this is the SerializeObject that I made:
// Save an object out to the disk
public static void SerializeObject<T>(this T toSerialize, String filename)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
TextWriter textWriter = new StreamWriter(filename);
xmlSerializer.Serialize(textWriter, toSerialize);
textWriter.Close();
}
There is no such builtin method as SerializeObject but it's not terribly difficult to code one up.
public void SerializeObject(this List<string> list, string fileName) {
var serializer = new XmlSerializer(typeof(List<string>));
using ( var stream = File.OpenWrite(fileName)) {
serializer.Serialize(stream, list);
}
}
And Deserialize
public void Deserialize(this List<string> list, string fileName) {
var serializer = new XmlSerializer(typeof(List<string>));
using ( var stream = File.OpenRead(fileName) ){
var other = (List<string>)(serializer.Deserialize(stream));
list.Clear();
list.AddRange(other);
}
}
These are my serialize/deserialize extension methods that work quite well
public static class SerializationExtensions
{
public static XElement Serialize(this object source)
{
try
{
var serializer = XmlSerializerFactory.GetSerializerFor(source.GetType());
var xdoc = new XDocument();
using (var writer = xdoc.CreateWriter())
{
serializer.Serialize(writer, source, new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") }));
}
return (xdoc.Document != null) ? xdoc.Document.Root : new XElement("Error", "Document Missing");
}
catch (Exception x)
{
return new XElement("Error", x.ToString());
}
}
public static T Deserialize<T>(this XElement source) where T : class
{
try
{
var serializer = XmlSerializerFactory.GetSerializerFor(typeof(T));
return (T)serializer.Deserialize(source.CreateReader());
}
catch //(Exception x)
{
return null;
}
}
}
public static class XmlSerializerFactory
{
private static Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>();
public static XmlSerializer GetSerializerFor(Type typeOfT)
{
if (!serializers.ContainsKey(typeOfT))
{
System.Diagnostics.Debug.WriteLine(string.Format("XmlSerializerFactory.GetSerializerFor(typeof({0}));", typeOfT));
var newSerializer = new XmlSerializer(typeOfT);
serializers.Add(typeOfT, newSerializer);
}
return serializers[typeOfT];
}
}
You just need to define a type for your list and use it instead
public class StringList : List<String> { }
Oh, and you don't NEED the XmlSerializerFactory, it's just there since creating a serializer is slow, and if you use the same one over and over this speeds up your app.
I'm not sure whether this will help you but I have dome something which I believe to be similar to you.
//A list that holds my data
private List<Location> locationCollection = new List<Location>();
public bool Load()
{
//For debug purposes
Console.WriteLine("Loading Data");
XmlSerializer serializer = new XmlSerializer(typeof(List<Location>));
FileStream fs = new FileStream("CurrencyData.xml", FileMode.Open);
locationCollection = (List<Location>)serializer.Deserialize(fs);
fs.Close();
Console.WriteLine("Data Loaded");
return true;
}
This allows me to deserialise all my data back into a List<> but i'd advise putting it in a try - catch block for safety. In fact just looking at this now is going to make me rewrite this in a "using" block too.
I hope this helps.
EDIT:
Apologies, just noticed you're trying to do it a different way but i'll leave my answer there anyway.
I was getting error while deserializing to object. The error was "There is an error in XML document (0, 0)". I have modified the Deserialize function originally written by #JaredPar to resolve this error. It may be useful to someone:
public static void Deserialize(this List<string> list, string fileName)
{
XmlRootAttribute xmlRoot = new XmlRootAttribute();
xmlRoot.ElementName = "YourRootElementName";
xmlRoot.IsNullable = true;
var serializer = new XmlSerializer(typeof(List<string>), xmlRoot);
using (var stream = File.OpenRead(fileName))
{
var other = (List<string>)(serializer.Deserialize(stream));
list.Clear();
list.AddRange(other);
}
}
Create a list of products be serialized
List<string> Products = new List<string>
{
new string("Product 1"),
new string("Product 2"),
new string("Product 3"),
new string("Product 4")
};
Serialization
using (FileStream fs = new FileStream(#"C:\products.txt", FileMode.Create))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, Products);
}
Deserialization
using (FileStream fs = new FileStream(#"C:\products.txt", FileMode.Open))
{
BinaryFormatter bf = new BinaryFormatter();
var productList = (List<string>)bf.Deserialize(fs);
}

Categories

Resources