First of all, I'm developing a WindowsPhone8.1 app which has a JSON (name, telephone number, email-...) file from the web. I store this JSON data in a list (contact is a class which has the parameters from the JSON.
I want to store (local no Cloud) this list on my WindowsPhone (its a List with marked favorites, not the whole list).
I tried it with StorageFolder and StorageFile, unfortunately, I failed. Got a problem with deleting and loading the files.
Developing in Microsoft Visual Studio Premium 2013 Update 3 with C#, Xaml.
I use this class, you need Json package which is available via nuget.
class StorageService : IConfigurationService
{
readonly StorageFolder _local = ApplicationData.Current.LocalFolder;
public async void Save<T>(string key, T obj)
{
var json = JsonConvert.SerializeObject(obj);
var dataFolder = await _local.CreateFolderAsync("DataFolder",
CreationCollisionOption.OpenIfExists);
var file = await dataFolder.CreateFileAsync(key,
CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, json);
}
public async Task<T> Load<T>(string key)
{
try
{
var dataFolder = await _local.GetFolderAsync("DataFolder");
var file = await dataFolder.OpenStreamForReadAsync(key);
string json;
using (var streamReader = new StreamReader(file))
{
json = streamReader.ReadToEnd();
}
return JsonConvert.DeserializeObject<T>(json);
}
catch (Exception)
{
return default(T);
}
}
}
Related
I have a UWP app that I am deploying as an AppPackage, and I am seeing an off error when I attempt to read/save a file in the LocalState folder. It does not happen everytime, so I am thinking the file is locked during the process at some point. The erroe is
No mapping for the Unicode character exists in the target multi-byte code page.
And the code that throws the error
private const string SETTINGS_FILENAME = "settings.json";
private static readonly StorageFolder _settingsFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
public async static Task<ConfigWrapper> LoadSettings()
{
try
{
StorageFile sf = await _settingsFolder.GetFileAsync(SETTINGS_FILENAME);
if (sf == null) return null;
string content = await FileIO.ReadTextAsync(sf, Windows.Storage.Streams.UnicodeEncoding.Utf8);
return JsonConvert.DeserializeObject<ConfigWrapper>(content);
}
catch (Exception e)
{
DiagnosticsClient.TrackException(e);
return null;
}
}
public async static Task<bool> SaveSettings(ConfigWrapper data)
{
try
{
StorageFile file = await _settingsFolder.CreateFileAsync(SETTINGS_FILENAME, CreationCollisionOption.ReplaceExisting);
string content = JsonConvert.SerializeObject(data, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings { });
await FileIO.WriteTextAsync(file, content, Windows.Storage.Streams.UnicodeEncoding.Utf8);
return true;
}
catch (Exception e)
{
DiagnosticsClient.TrackException(e);
return false;
}
}
I am making sure to save and read the file in UTF-8, so I am not sure why there would be an encoding error. Is there something I need to do (lock the file for instance) when I save/read?
Many applications save data in a proprietary format. The gibberish you provided is probably such.
Such applications often have a way to "write" or "export" or "save as" the data. Look for such.
I'm working with a 3rd-party developed Windows 8/8.1 App that relies on a Web service to retrieve data. We have the source code for the App but we don't have it for the web service, so I need to rebuild it. To complicate matters, I can't make changes to the App because it was sideloaded on a number of devices.
Even though I was able to decompile the web service's DLL, it didn't get me to a working point. The hurdle right now seems to be that XDocument.Load cannot process the returned stream.
Here's the AppCode:
public async Task<CustomerModel> ReadDataFromXml(string address)
{
var client = new HttpClient();
var response = await client.GetAsync(address);
// check that response was successful or throw exception
response.EnsureSuccessStatusCode();
var streamResponse = await response.Content.ReadAsStreamAsync();
var xDocumentObject = XDocument.Load(streamResponse);
var contents = from contact in xDocumentObject.Descendants(Constants.CUSTOMER_TAG)
select new {
...
I'm using VS 2013 to build the service with the built in MVC ASP.NET Web Application template which implements the ApiController.
The old code, after Serializing it with UTF8Encoding returned the results via
HttpContext.Current.Response.Write(strMessage);
The new code
public IEnumerable<Customer> Get(string activationcode)
{
return _handler.GetCustomerData(activationcode);
}
I believe I need this returned through the Get call which currently returns IEnumerable.
I believe I tried returning a string for the Get call
Another thing I tried was changing the Get statement to return a string and then return the XMLserialized version of the text.
private string Serialize(Customer cus)
{
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Customer));
XmlTextWriter xmlTextWriter = new XmlTextWriter((Stream)new MemoryStream(), Encoding.UTF8);
xmlSerializer.Serialize((XmlWriter)xmlTextWriter, (object)cus);
return this.UTF8ByteArrayToString(((MemoryStream)xmlTextWriter.BaseStream).ToArray());
}
catch (Exception ex)
{
return string.Empty;
}
}
But that returned "Data at the root level is invalid, Line 1, Position 1"
TLDR; Is there a way to get the GET Statement return the result so that XDocument.Load in our web app will function correctly?
Finally, figured it out:
Instead of returning the object, change the Get function to return HttpReponseMessage.
public HttpResponseMessage Get(string activationcode)
Serialize the returned data your object
Customer customer = _handler.GetCustomerData(activationcode).First();
string serializedCustomer = Serialize(customer);
and use StringContent to build your return:
return new HttpResponseMessage
{
Content = new StringContent(serializedCustomer, Encoding.UTF8, "text/xml")
};
Here's the functions used to serialized the data.
private string Serialize(Customer cus)
{
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Customer));
XmlTextWriter xmlTextWriter = new XmlTextWriter((Stream)new MemoryStream(), Encoding.UTF8);
xmlSerializer.Serialize((XmlWriter)xmlTextWriter, (object)cus);
return this.UTF8ByteArrayToString(((MemoryStream)xmlTextWriter.BaseStream).ToArray());
}
catch (Exception ex)
{
return string.Empty;
}
}
private string UTF8ByteArrayToString(byte[] characters)
{
return new UTF8Encoding().GetString(characters);
//return characters.ToString();
}
i have this two methods for writting and reading from the file.
public static async Task WriteDataToFileAsync(string fileName, string content)
{
byte[] data = Encoding.Unicode.GetBytes(content);
var folder = ApplicationData.Current.LocalFolder;
var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.OpenIfExists);
using (var s = await file.OpenStreamForWriteAsync())
{
await s.WriteAsync(data, 0, data.Length);
}
}
public async static Task<string> ReadFileContentsAsync()
{
var folder = ApplicationData.Current.LocalFolder;
try
{
var file = await folder.OpenStreamForReadAsync("MenuData.json");
using (var streamReader = new StreamReader(file))
{
Debug.WriteLine(streamReader.ReadToEnd());
return streamReader.ReadToEnd();
}
}
catch (Exception)
{
return string.Empty;
}
}
which are then used in this two methods
public static async void ApiToFileRestaurants()
{
HttpClient client = new HttpClient();
HttpResponseMessage response = client.GetAsync("http://bonar.si/api/restaurants").Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
await Restaurant.WriteDataToFileAsync("MenuData.json", responseBody);
}
public async static Task<List<Restaurant>> FileToRestaurantList()
{
var responseBody = await Restaurant.ReadFileContentsAsync();
List<Restaurant> parsedRestaurants = (List<Restaurant>)Newtonsoft.Json.JsonConvert.DeserializeObject(responseBody, typeof(List<Restaurant>));
return parsedRestaurants;
}
now my problem here is that ReadFileAsync doesn't return the results which i know are saved in MenuData.json file but instead returns empty string.
I was mostly getting source code for this from msdn
documentation.
Location of the file in my wp power tools looks like that.
I'm a novice programer so i might overlooked something else
Can you try to read the data from file asyncronously by using ReadToEndAsync which basically parses the complete data and sends response as one string.
var file = await folder.OpenStreamForReadAsync("MenuData.json");
using (var streamReader = new StreamReader(file))
{
return await streamReader.ReadToEndAsync();
}
Hope this helps!
i got the solution from one other forum
You're calling streamReader.ReadToEnd() twice. The first time you log
it to the Debug stream, the second is what you actually use as a
result. The method moves the file pointer to the end everytime it's
called and by the second time there's nothing to read.
so removing that debug line almost fixed my problem. I did get the string i wanted to but there was an error somewhere in it so Newtonsoft.Json had a hard time parsing it. So i tried #asitis solution and changed .json to .text and it worked
I am developing a windows application in 8.1 and I am getting a following error.
my application includes a procedure in which I will be moving a file from local storage to SD card.
My code is as follows
namespace MoveFile
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private async void btnCreateFolder_Click(object sender, RoutedEventArgs e)
{
await ReadFile();
//Error is showing here
**await WriteToFile();
}
public async Task WriteToFile()
{
// Get the text data from the textbox.
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(this.txtSafakCount.Text.ToCharArray());
//I got the error in this line.....showing interopservice exception
** StorageFolder knownFolder = await KnownFolders.RemovableDevices.CreateFolderAsync("bdfbdfb", CreationCollisionOption.ReplaceExisting);
StorageFolder sdCard = (await knownFolder.GetFoldersAsync()).FirstOrDefault();
// Create a new file named DataFile.txt.
var file = await sdCard.CreateFileAsync("kaaaaammmmfewfwmHoJa.txt", CreationCollisionOption.ReplaceExisting);
// Write the data from the textbox.
using (var s = await file.OpenStreamForWriteAsync())
{
s.Write(fileBytes, 0, fileBytes.Length);
}
}
public async Task ReadFile()
{
// Get the local folder.
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
if (local != null)
{
// Get the DataFolder folder.
var dataFolder = await local.GetFolderAsync("DataFolder");
// Get the file.
await dataFolder.CreateFileAsync("DataFile.txt", CreationCollisionOption.ReplaceExisting);
var file = await dataFolder.OpenStreamForReadAsync("DataFile.txt");
// Read the data.
using (StreamReader streamReader = new StreamReader(file))
{
this.txtSafakCount.Text = streamReader.ReadToEnd();
}
}
}
}
}
I want to know why this exception occurred and how it can be resolved.
Thanks in advance.
You are doing it wrong - first you should get SD card, then create folder. As the name KnownFolders.RemovableDevices says devices (not single device) - so you get the first of them as SD card (note that Universal Apps are not only for phones). So the working code can look like this:
// first get the SD card
StorageFolder sdCard = (await KnownFolders.RemovableDevices.GetFoldersAsync()).FirstOrDefault();
// then perform some actions - create folders, files ...
StorageFolder myFolder = await sdCard.CreateFolderAsync("bdfbdfb", CreationCollisionOption.ReplaceExisting);
Note also that you also need to add Capabilities in package.appxmanifest file, and Declarations if you want to use files (File Type Associations).
You will also find more help at MSDN.
I have a file Add.xaml
Inside, I have my recipe class that store my recipe object inside a list named breakfastRecipe
I then store the list beakfastRecipe inside a breakfast.json file. (using Deserialize/serialize methods i found from another member here)
I have a couple of textboxes where i write the name + ingredient then i use a save button that activate the json serializer. I used test method to test the deserializer if everything was working and it does. on the add.xaml page , the save and load of the object list is working perfectly
I have another page breakfastList.xaml where i want use longlistselector to populate my page with all my recipes. The problem is i don't how to access that breakfast.json file that was created on the other page.
I used the same get/deserializer to load my data
public async void btnGet_Tap()
{
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
try
{
// Getting JSON from file if it exists, or file not found exception if it does not
StorageFile textFile = await localFolder.GetFileAsync("breakfastList.json");
using (IRandomAccessStream textStream = await textFile.OpenReadAsync())
{
// Read text stream
using (DataReader textReader = new DataReader(textStream))
{
//get size
uint textLength = (uint)textStream.Size;
await textReader.LoadAsync(textLength);
// read it
string jsonContents = textReader.ReadString(textLength);
// deserialize back to our products!
//I only had to change this following line in this function
breakfastRecipe = JsonConvert.DeserializeObject<IList<Recipe>>(jsonContents) as List<Recipe>;
// and show it
//displayProduct();
}
}
}
catch (Exception ex)
{
tester.Text = "error";
}
}
When i write in the constructor
breakfastRecipe = new List();
btnGet_Tap();
In my mind, it i'm creating a new list then the btnGet load the data from that .json file that my add.xaml have created but it isn't working...
my question is then how to access the data on my "second page" which is stored in that .json file which was created by my first page
Try moving your btnGet_Tap code into an async function that returns a task and then await in the btnGet_Tap() method. You can then call
Foo().Wait();
if you wish to wait when calling in your constructor.
public async void btnGet_Tap()
{
await Foo();
}
public async Task Foo()
{
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
try
{
// Getting JSON from file if it exists, or file not found exception if it does not
StorageFile textFile = await localFolder.GetFileAsync("breakfastList.json");
using (IRandomAccessStream textStream = await textFile.OpenReadAsync())
{
// Read text stream
using (DataReader textReader = new DataReader(textStream))
{
//get size
uint textLength = (uint)textStream.Size;
await textReader.LoadAsync(textLength);
// read it
string jsonContents = textReader.ReadString(textLength);
// deserialize back to our products!
//I only had to change this following line in this function
breakfastRecipe = JsonConvert.DeserializeObject<IList<Recipe>>(jsonContents) as List<Recipe>;
// and show it
//displayProduct();
}
}
}
catch (Exception ex)
{
tester.Text = "error";
}
}