簡體   English   中英

從文件 C# UWP 中讀取 JSON

[英]Read JSON from file C# UWP

每次我嘗試從文件中讀取 JSON 時,都會出現錯誤。 如果我嘗試這樣做:

 JObject o1 = JObject.Parse(File.ReadAllText(@"c:\DB.json"));

或者它:

using (StreamReader file = File.OpenText(@"c:\db.json"))
            using(JsonTextReader reader = new JsonTextReader(file))
            {
                JObject o2 = (JObject)JToken.ReadFrom(reader);
            }

我收到這些錯誤

{"找不到文件 'c:\\DB.json'。":"c:\\DB.json"}

如果我將 DB.json 復制到 F: 目錄錯誤是:

{"找不到路徑 'f:\\DB.json' 的一部分。"}

我的問題是什么?

雖然我們可以在 UWP 應用中使用File.ReadAllText 方法File.OpenText 方法,但是 UWP 中的path參數有一些限制。 並非所有路徑都可以在這里使用。

默認情況下,應用程序可以訪問某些文件系統位置。 應用程序還可以通過文件選擇器或通過聲明功能來訪問其他位置。

有關詳細信息,請參閱文件訪問權限

對於像“c:\\DB.json”或“f:\\DB.json”這樣的文件路徑,默認情況下無法在 UWP 中訪問它們。 您可以先使用FileOpenPicker獲取訪問權限,然后將該項目添加到您的應用程序的FutureAccessList 中,以便您的應用程序將來可以輕松訪問該項目。

此外,在 UWP 應用中讀取文件,您可以參考創建、寫入和讀取文件

由於默認情況下所有應用程序都可以訪問應用程序安裝目錄和應用程序數據位置,因此我建議您將 json 文件放置/存儲在這兩個位置。 如果該文件隨您的應用程序一起提供並且不需要更改,您可以將它放在您的應用程序安裝目錄中。 為此,您可以在項目中添加該文件並將其Build Action設置為Content 但是安裝目錄中的文件是只讀的,如果您以后需要更改文件,您可以將其存儲在應用程序數據位置,例如ApplicationData.LocalFolder

當我嘗試從 JSON 讀取文件時,我會執行以下操作。

首先從 NuGet 下載 NewtonSoft.Json 包。

然后你可以使用下面的代碼。 (將字符串更改為要反序列化的對象)

private static string JsonFile = "myFile.dat";    //your file name

public static async Task<List<string>> LoadFromJsonAsync()
    {
        string JsonString = await DeserializeFileAsync(JsonFile);
        if (JsonString != null)
            return (List<string>)JsonConvert.DeserializeObject(JsonString, typeof(List<string>));
        return null;
    }

private static async Task<string> DeserializeFileAsync(string fileName)
    {
        try
        {
            StorageFile localFile = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
            return await FileIO.ReadTextAsync(localFile);
        }
        catch (FileNotFoundException)
        {
            return null;
        }
    }

我實現它的方式,在Jay Zuo 上面的回答Martin Tirion 的回答的幫助下,將所有內容都包含在一個靜態幫助器類中:

using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Pickers;
using static Windows.Storage.AccessCache.StorageApplicationPermissions;

/// <summary>
/// To deal with the process of ask user permission to read and write in folders
/// outside the App install, data locations and few others, and to remember it for the App lifecycle:
/// https://docs.microsoft.com/en-us/windows/uwp/files/file-access-permissions
/// </summary>
public static class DealAccessFiles
{
    /// <summary>
    /// As a suggestion because Guid.NewGuid() is unique ever; it can be another Token
    /// </summary>
    /// <param></param>
    /// <returns> string </returns>
    public static string TokenForFutureAccessList { get; } = Guid.NewGuid().ToString();
    /// <summary>
    /// As a suggestion; it can be another place, but this is owned by the App
    /// </summary>
    /// <param></param>
    /// <returns> StorageFolder </returns>
    public static StorageFolder LocalForStoreToken { get; } = ApplicationData.Current.LocalFolder;

    /// <summary>To remember a file or folder; it will use FutureAccessList.AddOrReplace(token, fileOrFolder):</summary>
    private static void RememberFileOrFolder(StorageFolder fileOrFolder, string token)
    {
        FutureAccessList.AddOrReplace(token, fileOrFolder); 
    }
    /// <summary>
    /// To retrieve the file the next time
    /// </summary>
    /// <param name="token"></param>
    /// <returns>StorageFile</returns>
    public static async Task<StorageFile> GetTokenForFile(string token)
    {
        if (FutureAccessList != null && FutureAccessList.ContainsItem(token)) 
            return await FutureAccessList.GetFileAsync(token);
        return null;
    }

    /// <summary>
    /// To retrieve the folder the next time
    /// </summary>
    /// <param name="token"></param>
    /// <returns>StorageFolder</returns>
    public static async Task<StorageFolder> GetTokenForFolder(string token)
    {
        if (FutureAccessList != null && FutureAccessList.ContainsItem(token)) 
            return await FutureAccessList.GetFolderAsync(token);
        return null;
    }

    //<summary>To forget a token, you can use this:</summary>
    public static async void ForgetTokenForFile(string token)
    {
        if (FutureAccessList != null && FutureAccessList.ContainsItem(token)) await Task.Run(() => FutureAccessList.Remove(token));
    }

    public static async Task<string> ReadOrCreateTokenFile(string tokenFileName, StorageFolder tokenFolder, string token="")
    {
        var storageFileNameToken = await tokenFolder.TryGetItemAsync(tokenFileName) as StorageFile;
        if (storageFileNameToken == null)
        {
            storageFileNameToken =
                await tokenFolder.CreateFileAsync(tokenFileName, CreationCollisionOption.ReplaceExisting);
            var customersFolderNameToken = string.IsNullOrWhiteSpace(token)?TokenForFutureAccessList:token;
            await FileIO.WriteTextAsync(storageFileNameToken, customersFolderNameToken);
            return customersFolderNameToken;
        }
        else
        {
            return await FileIO.ReadTextAsync(storageFileNameToken);
        }
    }
    public static async Task<StorageFolder> GetOrPickFolder(
                string folderNameToken
                , PickerLocationId suggestedStartLocation= PickerLocationId.DocumentsLibrary 
                , string fileTypeFilter = ".json")
    {
        var localFolderToReturn = await GetTokenForFolder(folderNameToken);
        if (localFolderToReturn != null) return localFolderToReturn;

        var picker = new FolderPicker
        {
            ViewMode = PickerViewMode.Thumbnail,
            SuggestedStartLocation = suggestedStartLocation
        };
        picker.FileTypeFilter.Add(fileTypeFilter);
        localFolderToReturn = await picker.PickSingleFolderAsync();
        RememberFileOrFolder(localFolderToReturn, folderNameToken);
        return localFolderToReturn;
    }
}

像這樣使用它:

  public class CustomerDataProvider
  {
    private const string CustomersFileName = "patients.json";
    private const string TokenFileName = "token.txt";
    private static StorageFolder _localFolder;
    private string _customersFolderNameToken;

    public async Task<IEnumerable<Customer>> LoadCustomersAsync()
    {
        List<Customer> customerList = null;

        //If it cannot read token, it will create one after you pick a Folder:
        _customersFolderNameToken = await DealAccessFiles.ReadOrCreateTokenFile(TokenFileName, DealAccessFiles.LocalForStoreToken); 

        //After read or create and storing it on FutureAccessList, now it can access the Folder
        _localFolder = await DealAccessFiles.GetOrPickFolder(_customersFolderNameToken);

        var storageFile = await _localFolder.TryGetItemAsync(CustomersFileName) as StorageFile;
        if (storageFile == null)
        {
            customerList = new List<Customer>
          {
              new Customer{FirstName="Joe",LastName="Doe"},
              new Customer{FirstName="Joe",LastName="Doe Jr"},
              ...,
          };
        }
        else
        {
            using (var stream = await storageFile.OpenAsync(FileAccessMode.Read))
            {
                //Read it, in my case, JSON that I need to share with Windows Forms
                //and VBA(!) Projects
            }
        }
        return customerList;
   }

現在,保存文件沒有問題:

public async Task SaveCustomersAsync(IEnumerable<Customer> customers)
{
  var storageFile = await _localFolder.CreateFileAsync(CustomersFileName, CreationCollisionOption.ReplaceExisting);

  using (var stream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
  {
       //Save the way U need
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM