Deserializing an array of values from JSON in 2048 game causes NullReferenceException - c#

I'm making a 2048 clone for my university classes. I've got a custom class:
public class Field
{
public int value;
public bool stop;
}
And an array of those fields:
public Field[,] gameArray = new Field[4, 4];
Those are in a custom class.
Here are the methods I use to save and load this array in the item page cs file:
private async void saveAsync()
{
string jsonContents = JsonConvert.SerializeObject(currentGame.gameArray);
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile textFile = await localFolder.CreateFileAsync(saveFileName,
CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream textStream = await textFile.OpenAsync(FileAccessMode.ReadWrite))
{
using (DataWriter textWriter = new DataWriter(textStream))
{
textWriter.WriteString(jsonContents);
await textWriter.StoreAsync();
}
}
}
private async void loadAsync()
{
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile textFile = await localFolder.GetFileAsync(saveFileName);
using (IRandomAccessStream textStream = await textFile.OpenReadAsync())
{
using (DataReader textReader = new DataReader(textStream))
{
if ((uint)textStream.Size != 0)
{
uint textLength = (uint)textStream.Size;
await textReader.LoadAsync(textLength);
string jsonContents = textReader.ReadString(textLength);
currentGame.gameArray = JsonConvert.DeserializeObject<Field[,]>(jsonContents);
}
else currentGame.Reset();
}
update();
}
}
currentGame is an instance of the custom class. Reset(); method, well, resets all the fields, sets their value to 0. Update(); method is used to check if the player lost(not yet implemented), runs the saveAsync(); method and fills the board with lines like this(color method just changes the background color):
txt10.Text = currentGame.gameArray[0, 0].value.ToString();
color(b10, currentGame.gameArray[0, 0].value);
and the first line is what gives me a NullReferenceException. I probably messed something up with serialization and deserialization of the array, but I got literally no idea what it is and how to fix that, that's my first take on serialization and I basically copied it from the internet. Do I somehow need to pass a reference to the loadAsync(); method?

Related

How to pass xml file inside of code instead of a folder path?

I'am trying to not create a file, and pass xml document straight to a SkiaSharp method Load. I mean, is there the way to imitate a path? So here is the code:
public IActionResult svgToPng(string itemId, string mode = "
{
var svgSrc = new XmlDocument();
svgSrc.LoadXml(/*Some xml code*/);
string svgSaveAs = "save file path";
var quality = 100;
var svg = new SkiaSharp.Extended.Svg.SKSvg();
var pict = svg.Load(svgSrc); // HERE, it needs to be a path, not XmlDocument, but i want to pass straight
var dimen = new SkiaSharp.SKSizeI
(
(int) Math.Ceiling(pict.CullRect.Width),
(int) Math.Ceiling(pict.CullRect.Height)
);
var matrix = SKMatrix.MakeScale(1, 1);
var img = SKImage.FromPicture(pict, dimen, matrix);
// Convert to PNG
var skdata = img.Encode(SkiaSharp.SKEncodedImageFormat.Png, quality);
using(var stream = System.IO.File.OpenWrite(svgSaveAs))
{
skdata.SaveTo(stream);
}
ViewData["Content"] = "PNG file was created out of SVG.";
return View();
}
The Load method seems to be this:
public SKPicture Load(
using (var stream = File.OpenRead(filename))
{
return Load(stream);
}
}
look at the code of that library :
https://github.com/mono/SkiaSharp.Extended/blob/master/SkiaSharp.Extended.Svg/source/SkiaSharp.Extended.Svg.Shared/SKSvg.cs
Look at the Load method, it has multiple implementations :
public SKPicture Load(string filename)
{
using (var stream = File.OpenRead(filename))
{
return Load(stream);
}
}
public SKPicture Load(Stream stream)
{
using (var reader = XmlReader.Create(stream, xmlReaderSettings, CreateSvgXmlContext()))
{
return Load(reader);
}
}
public SKPicture Load(XmlReader reader)
{
return Load(XDocument.Load(reader));
}
You will need to pick one of them and use it. Now, nothing stops you from getting the code and adding one extra Load for an XML string for example, but since this is a library you do not control, I'd stick to what you are given.
You could use the XmlReader version, that's probably the closest one to what you want.

Append (n) to file names (not using system.io.file) to ensure unique names

I have a class that holds the name of a file, and the data of a file:
public class FileMeta
{
public string FileName { get; set; }
public byte[] FileData { get; set; }
}
I have a method that populates a collection of this class through an async download operation (Files are not coming from a local file system):
async Task<List<FileMeta>> ReturnFileData(IEnumerable<string> urls)
{
using (var client = new HttpClient())
{
var results = await Task.WhenAll(urls.Select(async url => new
{
FileName = Path.GetFileName(url),
FileData = await client.GetByteArrayAsync(url),
}));
return results.Select(result =>
new FileMeta
{
FileName = result.FileName,
FileData = result.FileData
}).ToList();
}
}
I am going to feed this List<FileMeta> into a ZipFile creator, and the ZipFile like all File containers needs unique file names.
Readability is important, and I would like to be able to do the following:
file.txt => file.txt
file.txt => file(1).txt
file.txt => file(2).txt
There are a number of examples on how to do this within the file system, but not with a simple object collection. (Using System.IO.File.Exists for example)
What's the best way to loop through this collection of objects and return a unique set of file names?
How far I've gotten
private List<FileMeta> EnsureUniqueFileNames(IEnumerable<FileMeta> fileMetas)
{
var returnList = new List<FileMeta>();
foreach (var file in fileMetas)
{
while (DoesFileNameExist(file.FileName, returnList))
{
//Append (n) in sequence until match is not found?
}
}
return returnList;
}
private bool DoesFileNameExist(string fileName, IEnumerable<FileMeta> fileMeta)
{
var fileNames = fileMeta.Select(file => file.FileName).ToList();
return fileNames.Contains(fileName);
}
You can try the following to increment the filenames:
private List<FileMeta> EnsureUniqueFileNames(IEnumerable<FileMeta> fileMetas)
{
var returnedList = new List<FileMeta>();
foreach (var file in fileMetas)
{
int count = 0;
string originalFileName = file.FileName;
while (returnedList.Any(fileMeta => fileMeta.FileName.Equals(file.FileName,
StringComparison.OrdinalIgnoreCase))
{
string fileNameOnly = Path.GetFileNameWithoutExtension(originalFileName);
string extension = Path.GetExtension(file.FileName);
file.FileName = string.Format("{0}({1}){2}", fileNameOnly, count, extension);
count++;
}
returnList.Add(file);
}
return returnList;
}
As a side note, in your ReturnFileData, you're generating two lists, one of anonymous type and one of your actual FileMeta type. You can reduce the creation of the intermediate list. Actually, you don't need to await inside the method at all:
private Task<FileMeta[]> ReturnFileDataAsync(IEnumerable<string> urls)
{
var client = new HttpClient();
return Task.WhenAll(urls.Select(async url => new FileMeta
{
FileName = Path.GetFileName(url),
FileData = await client.GetByteArrayAsync(url),
}));
}
I made the return type a FileMeta[] instead of a List<FileMeta>, as it is a fixed sized returning anyway, and reduces the need to call ToList on the returned array. I also added the Async postfix, to follow the TAP guidelines.

calling an async task in a windows store app?

I had a working "save reader" that would strip unwanted data out of a winform, then return a list of strings referred to as "items" .
My problem is that when converting this for use with a winstore app; I cannot call an async task, and I need to use one to access the file.
im not 100% sure how to do this, so any help is appreciated; this is what I have so far.
the public SaveGame(StorageFile file)
{
File = promptUser();
}
throws an error, I have tried making the constructor async to, but that throws more errors
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Windows.Storage;
using Windows.Storage.Pickers;
namespace save_game_reader
{
class SaveGame
{
private StorageFile File { get; set; }
public SaveGame(StorageFile file)
{
File = promptUser();
Task<StorageFile> returnedTaskTResult = promptUser();
StorageFile intResult = await promptUser();
}
public async Task<List<string>> GetAllItems()
{
string WholeSaveString = await Windows.Storage.FileIO.ReadTextAsync(File);
List<string> ToReturn = new List<string>();
List<string> SplitList = WholeSaveString.Split(new string[] { "data" }, StringSplitOptions.None).ToList();
foreach (string line in SplitList)
{
var start = line.IndexOf("value") + 6;
var end = line.IndexOf("type", start);
if (end != -1)
{
string substring = line.Substring(start, (end - start) - 8);
if (substring.Length >= 4)
{
ToReturn.Add(substring);
}
}
}
return ToReturn;
}
private async Task<StorageFile> promptUser()
{
var picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
StorageFile sf = await picker.PickSingleFileAsync();
return sf;
}
}
}
It's pretty rare to need to do so, but the typical way to work around constructors in async is to just use a static method:
class SaveGame
{
SaveGame(StorageFile file)
{
File = file;
}
public async Task<SaveGame> Create()
{
return new SaveGame(await promptUser());
}
}
SaveGame g = await SaveGame.Create();
It may be worth it in your case to consider a cleaner separation between UI code and backend code. If something seems weird or difficult to do, it's often a sign of design flaw.
Also, one does not "call an async task". You call an async method and await a Task.
Constructors cannot be async, change your code as follows:
public SaveGame()
{}
public async Task Init(Storage file)
{
File = promptUser();
Task<StorageFile> returnedTaskTResult = promptUser();
StorageFile intResult = await promptUser();
}
somewhere else in an async method you call it like this:
var save = new SaveGame();
await save.Init(file);

Cannot deserialize Image after sending it through the network

So, I have these two methods, which I am using to serialize and deserialize Images:
private static Image GetImageFromString(string image)
{
using (var stream = new MemoryStream(Convert.FromBase64String(image)))
{
return Image.FromStream(stream);
}
}
private static string GetImageAsString(Image image)
{
using (var stream = new MemoryStream())
{
image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
return Convert.ToBase64String(stream.GetBuffer());
}
}
If I do something Like this:
public Form1()
{
InitializeComponent();
var image = Image.FromFile(#"F:\phpide.png");
pictureBox1.Image = image;
var serialized = GetImageAsString(image);
var secondImage = GetImageFromString(serialized);
pictureBox2.Image = secondImage;
}
It works as expected
Although, If I do something like this:
//client
public void GetImage(JObject o)
{
var imageFile = o["file"].ToString();
if (!File.Exists(imageFile))
{
SendMessage("File does not exist");
return;
}
using (var image = Image.FromFile(imageFile))
{
var serialized = GetImageAsString(image);
var ob = new JObject
{
{ COMMAND, (int) Command.GetImage },
{ "content", serialized }
};
Send(ob);
ob = null;
serialized = null;
}
}
//server
public void ReceiveImage(JObject o)
{
var content = o["content"].ToString();
var image = GetImageFromString(content);
var form = new ImagePreviewForm(image);
form.Show();
}
//server
public ImagePreviewForm(Image image)
{
InitializeComponent();
pictureBox1.Image = image;
}
The image is just blank.
I have checked and the image is being received correctly, with no data loss.
What could be going wrong here? Where should I look?
This is at least one problem:
return Convert.ToBase64String(stream.GetBuffer());
You shouldn't use MemoryStream.GetBuffer here - you should use ToArray. The GetBuffer method returns the underlying buffer as-is... complete with junk data at the end of the buffer, beyond the logical current length of the stream.
Additionally, you shouldn't close the stream when you call Image.FromStream. From the docs:
You must keep the stream open for the lifetime of the Image.
So get rid of the using statement in GetImageFromString.
With the using statement you are disposing the image before the UI thread can display the image properly. Take the code out of the using block and add a Dispose() statement to the Form.Close() method.

How to display images from Picture Directory ?

I want to display pictures in the Pictures library. I get pictures and bind data.
StorageFolder picturesFolder = KnownFolders.PicturesLibrary;
IReadOnlyList<StorageFile> myPictures = await picturesFolder.GetFilesAsync();
var mydata = from file in myPictures select new { Subtitle = "subtitle", Title = "title", Image = this.getImage(file.Path) };
this.DefaultViewModel["Items"] = mydata;
This is getImage() for set BitmapImage.
private async Task<BitmapImage> getImage(string finename)
{
StorageFolder picturesFolder = KnownFolders.PicturesLibrary;
StorageFile file = await picturesFolder.GetFileAsync(fileName);
var stream = await file.OpenReadAsync();
var bitmap = new BitmapImage();
bitmap.SetSource(stream);
return bitmap;
}
But pictures are not displayed.I think this is because of async function, but I don't know the solution. Could you help me ?
I'm not sure how do you use the data that set to DefaultViewModel, but yeah, it looks like the async method is your problem.
What you need to do is to somehow await each call to getImage(). One way to do this is to use async lambda in your select. But to do that, you need to use the method syntax.
When you do that, you will have IEnumerable<Task<a>> (where a is your anonymous type), but you need just IEnumerable<a>. To get that, use Task.WhenAll() (which will return Task<a[]>) and then await its result:
var tasks = myPictures.Select(
async file => new { Subtitle = "subtitle", Title = "title", Image = await getImage(file.Path) });
var data = await Task.WhenAll(tasks);
This will execute all getImage()s at once, which may not be the most efficient solution. If you don't want that, you would need different solution.
svick's solution seems like should work, but as he/she said - it might not be the most efficient solution. A better solution for a folder with unknown number of files is to use data virtualization with FileInformationFactory.GetVirtualizedFilesVector(). That works best with a converter.
Something I have used:
Getting a virtualized file list and binding to a ListView
private async void GetPicturesFromGalleryFolder()
{
var queryOptions = new QueryOptions();
queryOptions.FolderDepth = FolderDepth.Shallow;
queryOptions.IndexerOption = IndexerOption.UseIndexerWhenAvailable;
queryOptions.SortOrder.Clear();
var sortEntry = new SortEntry {PropertyName = "System.DateModified", AscendingOrder = false};
queryOptions.SortOrder.Add(sortEntry);
queryOptions.FileTypeFilter.Add(".png");
var fileQuery = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);
var fileInformationFactory =
new FileInformationFactory(
fileQuery,
ThumbnailMode.PicturesView,
0,
ThumbnailOptions.None,
true);
MyListView.ItemsSource = fileInformationFactory.GetVirtualizedFilesVector();
}
XAML
<ListView.ItemTemplate>
<DataTemplate>
<Image
Source="{Binding Converter={StaticResource converters:IStorageItemInformationToBitmapImageConverter}"/>
</DataTemplate>
</ListView.ItemTemplate>
IStorageItemInformationToBitmapImageConverter
public class IStorageItemInformationToBitmapImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var fileInfo = value as FileInformation;
if (fileInfo != null)
{
var bi = new BitmapImage();
// The file is being opened asynchronously but we return the BitmapImage immediately.
SetSourceAsync(bi, fileInfo);
return bi;
}
return null;
}
private async void SetSourceAsync(BitmapImage bi, FileInformation fi)
{
try
{
using (var stream = await fi.OpenReadAsync())
{
await bi.SetSourceAsync(stream);
}
}
catch
{
// ignore failure
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}

Categories