Google Code Prettify

顯示具有 JSON 標籤的文章。 顯示所有文章
顯示具有 JSON 標籤的文章。 顯示所有文章

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot implicitly convert type 'Newtonsoft.Json.Linq.JArray' to 'System.Collections.Generic.List''

這個錯誤 Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot implicitly convert type 'Newtonsoft.Json.Linq.JArray' to 'System.Collections.Generic.List' 是由於您嘗試將一個 JArray 轉換為 List 引起的。Newtonsoft.Json.Linq.JArray 是 Newtonsoft.Json 庫中的一個類型,表示 JSON 陣列,而 List 是一個泛型列表。


要解決這個問題,您需要顯式地將 JArray 轉換為 List。這可以通過 JsonConvert 類的 DeserializeObject 方法來實現。


以下是具體的做法:


假設 JSON 形式
假設您從某個地方獲得的 JSON 是這樣的:

json
[
    {
        "Id": 1,
        "Name": "Company A",
        "Location": "Location A"
    },
    {
        "Id": 2,
        "Name": "Company B",
        "Location": "Location B"
    }
]



模型類 tblCompany
首先,確保您的模型類 tblCompany 是這樣的:

csharp
namespace WebModel.Models
{
    public class tblCompany
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Location { get; set; }
    }
}



反序列化 JSON
以下是從 JArray 反序列化為 List 的範例:

csharp
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using WebModel.Models;

public class Program
{
    public static void Main()
    {
        // 假設您獲得了一個 JSON 陣列
        string jsonArray = "[{\"Id\":1,\"Name\":\"Company A\",\"Location\":\"Location A\"},{\"Id\":2,\"Name\":\"Company B\",\"Location\":\"Location B\"}]";

        // 將 JSON 字符串解析為 JArray
        JArray jArray = JArray.Parse(jsonArray);

        // 反序列化為 List<tblCompany>
        List<tblCompany> companies = jArray.ToObject<List<tblCompany>>();

        // 使用反序列化的數據
        foreach (var company in companies)
        {
            Console.WriteLine($"Id: {company.Id}, Name: {company.Name}, Location: {company.Location}");
        }
    }
}




注意事項
引用 Newtonsoft.Json:確保您的專案中引用了 Newtonsoft.Json 庫。如果沒有引用,可以使用 NuGet 包管理器安裝:

bash
dotnet add package Newtonsoft.Json

JSON 格式一致:確保 JSON 字符串的結構與 tblCompany 類中的屬性對應。



總結

要將 JArray 轉換為 List<WebModel.Models.tblCompany>,您需要使用 ToObject<List<tblCompany>>() 方法來反序列化 JSON 陣列。這樣可以正確地將 JSON 陣列轉換為所需的列表類型,避免類型轉換錯誤。

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot implicitly convert type 'Newtonsoft.Json.Linq.JObject' to 'WebModel.Models.tblCompany''

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot implicitly convert type 'Newtonsoft.Json.Linq.JObject' to 'WebModel.Models.tblCompany'' 這個錯誤表示您正在嘗試將 JObject 類型的值隱式轉換為 tblCompany 類型。這通常是由於您直接將 JSON 物件指派給一個強類型物件引起的。

為了解決這個問題,您需要顯式地將 JObject 轉換為 tblCompany。這可以通過 JsonConvert 類的 ToObject 方法來實現。以下是具體的範例:

假設 JSON 物件

假設您從某個地方獲得的 JSON 是這樣的:

json
{
    "Id": 1,
    "Name": "Company A",
    "Location": "Location A"
}



模型類 tblCompany

首先,確保您的模型類 tblCompany 是這樣的:

csharp
namespace WebModel.Models
{
    public class tblCompany
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Location { get; set; }
    }
}



反序列化 JSON

以下是從 JObject 反序列化為 tblCompany 的範例:

csharp
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WebModel.Models;

public class Program
{
    public static void Main()
    {
        // 假設您獲得了一個 JSON 物件字符串
        string jsonObject = "{\"Id\":1,\"Name\":\"Company A\",\"Location\":\"Location A\"}";

        // 將 JSON 字符串解析為 JObject
        JObject jObject = JObject.Parse(jsonObject);

        // 反序列化為 tblCompany
        tblCompany company = jObject.ToObject();

        // 使用反序列化的數據
        Console.WriteLine($"Id: {company.Id}, Name: {company.Name}, Location: {company.Location}");
    }
}



注意事項

引用 Newtonsoft.Json:確保您的專案中引用了 Newtonsoft.Json 庫。如果沒有引用,可以使用 NuGet 包管理器安裝:

bash

dotnet add package Newtonsoft.Json

JSON 格式一致:確保 JSON 字符串的結構與 tblCompany 類中的屬性對應。

完整範例
以下是完整的程式碼範例,包括 tblCompany 類和 JSON 反序列化範例:

csharp
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WebModel.Models;

namespace JsonConvertExample
{
    public class Program
    {
        public static void Main()
        {
            // 假設您獲得了一個 JSON 物件字符串
            string jsonObject = "{\"Id\":1,\"Name\":\"Company A\",\"Location\":\"Location A\"}";

            // 將 JSON 字符串解析為 JObject
            JObject jObject = JObject.Parse(jsonObject);

            // 反序列化為 tblCompany
            tblCompany company = jObject.ToObject();

            // 使用反序列化的數據
            Console.WriteLine($"Id: {company.Id}, Name: {company.Name}, Location: {company.Location}");
        }
    }

    public class tblCompany
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Location { get; set; }
    }
}



這個範例展示了如何從 JObject 反序列化為 tblCompany 類型,避免了類型轉換錯誤。這樣可以確保 JSON 物件正確地轉換為所需的強類型物件。

[Web API]讓 Web API 預設傳回 JSON 字串

在 \App_Start\WebApiConfig.cs 中加二行程式,加上之後,以後所有回傳的資料就都會是 JSON 字串


using System.Web.Http;
using System.Linq;

namespace TokenTest
{
    public static class WebApiConfig {
        public static void Register(HttpConfiguration config) {
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
            config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
        }
    }
}

之後不必用 string s = JsonConvert.SerializeObject(listUserInfo) ,回傳的範例程式如下:

[HttpGet]
[Route("api/User/GetAllUserInfo")]
public List<UserInfo> GetAllAsync()
{
 List<UserInfo> listUserInfo = new List<UserInfo>();
 var db = new TokenDbContext();
 var usr = db.User.SqlQuery("select * from Users where Name='Morse'");
 foreach(User u in usr) {
  listUserInfo.Add(new UserInfo {
   Id = u.Id.ToString(),
   Name = u.Name,
   Password = u.Password
  });
 }
 return listUserInfo;
}

Rest let  執行結果:


Chrome 執行結果:

Firefox 執行結果:

[C#]JSON資料轉換範例

本範例的資料轉換採用的是 JSON.NET 套件。

Source Code
using System;

using Newtonsoft.Json;

namespace ConsoleApp1
{
    public class TestUser
    {
        public string UserName { get; set; }
        public string UserPassword { get; set; }
        public string UserMail { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            TestUser u = new TestUser {
                UserName = "morse",
                UserPassword = "1234567",
                UserMail = "morsecheng@gmail.com"
            };

            string a = JsonConvert.SerializeObject(u);
            TestUser b = JsonConvert.DeserializeObject(a);
            string c = JsonConvert.SerializeObject(b);

            Console.WriteLine(a);
            Console.WriteLine("------------");
            Console.WriteLine(b);
            Console.WriteLine(b.UserName);
            Console.WriteLine(b.UserMail);
            Console.WriteLine(b.UserPassword);
            Console.WriteLine("------------");
            Console.WriteLine(c);

            Console.WriteLine("Press any key...");
            Console.ReadKey();
        }
    }
}




Result




前端 MVC 框架

前端獲取 JSON 數據作為 Model,那麼 MVC 中的 View 和 Controller 又各自在前端扮演什麼角色呢?這就需要一套完整的框架作為解決方案了。現在市面上流行的前端 MVC 框架大概有下面幾種

  • Backbone.js. 應該是比較早出現的一個框架了,風靡於 Ruby on Rails 社區,包括 LinkedIn, Hulu, WordPress.com, Foursquare, Bitbucket, Disqus, Groupon, Basecamp, Pandora 以及國內的豆瓣,雪球,很多網站都使用了它,加上有 37signal 的背景,可謂是頗有知名度。它比較輕量,性能不錯,但正因為如此導致它缺失了很多前端需要的重要組件,你必須自己寫很多代碼來實現,隨着應用越來越複雜,要在更深層次嵌入視圖,惡夢 開始了……另外我對它主頁的文檔展現形式有點不爽,一個腦袋裡從來沒有前端 MVC 概念的人去讀,完全不知所云。

  • Knockout. 一個很強大的 MVVM 框架,如果你是 .NET 開發者你會很熟悉這種做法。它實現了 DOM 元素和 JS model 之間的雙向綁定,也就是說你在表單中的每一個輸入,都會實時更新到 model 里,最後提交表單也就等同於更新 model 的動作,一切很順暢。官方主頁上的例子也很容易理解。

  • AngularJS. 來自 Google,項目主頁直接就是 demo,讓你直接快速上手大呼過癮。乍一看讓人覺得很簡單,不過進了門之後你會發現後面的路還很長。它製造了很多概念,對於前端來講可以說是醍醐灌頂,比如它把 unix 里的 | (管道)搬到了前端……一開始我發現 AngularJS 和 Knockout 一樣在 HTML 中寫私有屬性立即就否決了它,後來發現原來它也支持 HTML5 標準的 data-* 寫法,於是又重新愛上了它。如果你習慣 Twitter Bootstrap 的應用方式我想你也會喜歡它的。

  • Ember.js. 比較有前途的一個框架,模板很好用,開發效率首屈一指。它有很多強制性約束,可以幫你自動完成很多事情。相比 Angular 上手可能要難一點,Angular 一開始不需要這麼費勁也能做一些讓人興奮不已的事兒,而 Ember 缺少這種前期興奮點。另外最令人不爽的是 Ember 的體積,min+gzip 壓縮之後還有 55K,太龐大了。

  • Batman.js. 適合 RoR 開發,沒怎麼用過。看上去和 Knockout.js 很像,提供了自動生成代碼的一些工具。整個庫壓縮後 40K 也比較大。

  • CanJS. 也沒用過,社區規模小。


那麼,到底該選哪個框架呢?我的建議是選一個項目,去看一下各個框架的實現,誰的代碼量少,誰的結構清晰,誰的實現方式讓你感覺比較爽,自然就有答案了。這不,在 Github 上早已經有國際友人分別用各種框架實現了經典的 todo 應用,方便大家比較。就我個人而言,我選擇了 AngularJS,用 AngularJS 代替 Backbone,代碼減少了一半。雖然性能可能有點問題,但面向未來嘛,瀏覽器會越來越強大的。

[WebAPI]將 JSON 字串 POST 到 Web API

要將 JSON 編碼內容 POST 到 Web API, 首先要先準備一支方法程式 (method),用 JSON.NET 將資料轉換成 JSON 格式的字串資料,再利用 HttpClient 將它送到 Web API (遠端伺服器端) ,這一支方法程式的範例如下: ( JsonPostAsync method)


private static async Task<apiresult> JsonPostAsync(APIData apiData)
{
    APIResult fooAPIResult;
    using (HttpClientHandler handler = new HttpClientHandler())
    {
        using (HttpClient client = new HttpClient(handler))
        {
            try
            {
                #region 呼叫遠端 Web API
                string FooUrl = $"http://localhost:8089/api/Values"; 
                HttpResponseMessage response = null;

                #region  設定相關網址內容
                var fooFullUrl = $"{FooUrl}";

                // Accept 用於宣告客戶端要求服務端回應的文件型態 (底下兩種方法皆可任選其一來使用)
                //client.DefaultRequestHeaders.Accept.TryParseAdd("application/json");
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                // Content-Type 用於宣告遞送給對方的文件型態
                //client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");

                var fooJSON = JsonConvert.SerializeObject(apiData);
                // https://msdn.microsoft.com/zh-tw/library/system.net.http.stringcontent(v=vs.110).aspx
                using (var fooContent = new StringContent(fooJSON, Encoding.UTF8, "application/json"))
                {
                    response = await client.PostAsync(fooFullUrl, fooContent);
                }
                #endregion
                #endregion

                #region 處理呼叫完成 Web API 之後的回報結果
                if (response != null)
                {
                    if (response.IsSuccessStatusCode == true)
                    {
                        // 取得呼叫完成 API 後的回報內容
                        String strResult = await response.Content.ReadAsStringAsync();
                        fooAPIResult = JsonConvert.DeserializeObject<apiresult>(strResult, new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore });
                    }
                    else
                    {
                        fooAPIResult = new APIResult
                        {
                            Success = false,
                            Message = string.Format("Error Code:{0}, Error Message:{1}", response.StatusCode, response.RequestMessage),
                            Payload = null,
                        };
                    }
                }
                else
                {
                    fooAPIResult = new APIResult
                    {
                        Success = false,
                        Message = "應用程式呼叫 API 發生異常",
                        Payload = null,
                    };
                }
                #endregion
            }
            catch (Exception ex)
            {
                fooAPIResult = new APIResult
                {
                    Success = false,
                    Message = ex.Message,
                    Payload = ex,
                };
            }
        }
    }

    return fooAPIResult;
}


接下來,我們須在控制器 (Controller) 中加上一個 Post 的方法 (method),

[HttpPost]
public APIResult Post([FromBody]APIData value)
{
    APIResult foo;

    if (value.Id == 777)
    {
        foo = new APIResult()
        {
            Success = true,
            Message = "透過 post 方法,接收到 Id=777 資料",
            Payload = value
        };
    }
    else
    {
        foo = new APIResult()
        {
            Success = false,
            Message = "無法發現到指定的 ID",
            Payload = null
        };
    }
    return foo;
}


在這個 Post 方法中,我們帶入一個 APIData 型別的物件當物件中 Id 這個屬性值為 777 的時候,在 Web API 的動作將會回覆這次的呼叫是成功的且傳回資料型別為 APIData 的 value 物件,否則,會回覆此次呼叫失敗(無法發現到指定的 ID),並傳回 null。


在 Post 的方法實作完成後,接著我們要準備程式的進入點方法(Main method)。


static void Main(string[] args)
{
    var fooAPIData = new APIData()
    {
        Id = 777,
        Name = "VulcanSource",
    };
    var foo = JsonPostAsync(fooAPIData).Result;
    Console.WriteLine($"使用 JSON 格式與使用 Post 方法呼叫 Web API 的結果");
    Console.WriteLine($"結果狀態 : {foo.Success}");
    Console.WriteLine($"結果訊息 : {foo.Message}");
    Console.WriteLine($"Payload : {foo.Payload}");
    Console.WriteLine($"");

    Console.WriteLine($"Press any key to Exist...{Environment.NewLine}");
    Console.ReadKey();

    fooAPIData = new APIData()
    {
        Id = 123,
        Name = "VulcanSource",
    };
    foo = JsonPostAsync(fooAPIData).Result;
    Console.WriteLine($"使用 JSON 格式與使用 Post 方法呼叫 Web API 的結果");
    Console.WriteLine($"結果狀態 : {foo.Success}");
    Console.WriteLine($"結果訊息 : {foo.Message}");
    Console.WriteLine($"Payload : {foo.Payload}");
    Console.WriteLine($"");

    Console.WriteLine($"Press any key to Exist...{Environment.NewLine}");
    Console.ReadKey();
}


方法中我們分為兩個部分,先是傳送  Id 這個屬性值為 777 的資料到 Web API ,接著再傳送  Id 這個屬性值為 123 的資料到 Web API ,看看這兩個部分執行後得到的結果是不是如預期。


在此,執行結果我們就不再演示,希望此文章能對你有幫助。




產生JSON字串的幾種方式整理(2)

4. 物件序列化
這個要先知道輸出的json字串長什麼樣子
貼到http://json2csharp.com/去產生類別程式碼
image
然後

01protected void Page_Load(object sender, EventArgs e)
02{
03    if (!IsPostBack)//Get Method
04    {
05        //準備資料
06        string collapse_key = "score_update";
07        int time_to_live = 108;
08        bool delay_while_idle = true;
09        string score = "4x8";
10        string time = "15:16.2342";
11        List<string> registration_ids = new List<string>() { "4","8","15","16","23","42"};
12 
13          
14        //建立物件,塞資料
15        RootObject root = new RootObject();
16        root.collapse_key = collapse_key;
17        root.time_to_live = time_to_live;
18        root.delay_while_idle = delay_while_idle;
19        Data data = new Data();
20        root.data = data;
21        root.data.score = score;
22        root.data.time = time;
23        root.registration_ids = registration_ids;
24        //物件序列化
25        string strJson = JsonConvert.SerializeObject(root, Formatting.Indented);
26        //輸出結果
27        Response.Write(strJson);
28    }
29}
或使用物件初始化方式塞值
01protected void Page_Load(object sender, EventArgs e)
02{
03    if (!IsPostBack)//Get Method
04    {
05        //準備資料
06        string collapse_key = "score_update";
07        int time_to_live = 108;
08        bool delay_while_idle = true;
09        string score = "4x8";
10        string time = "15:16.2342";
11        List<string> registration_ids = new List<string>() { "4","8","15","16","23","42"};
12 
13          
14        //建立物件,塞資料
15        RootObject root = new RootObject()
16        {
17            collapse_key = collapse_key,
18            time_to_live = time_to_live,
19            delay_while_idle = delay_while_idle,
20            data = new Data()
21            {
22               score = score,
23               time=time
24            },
25            registration_ids = registration_ids
26        };
27         
28        //物件序列化
29        string strJson = JsonConvert.SerializeObject(root, Formatting.Indented);
30        //輸出結果
31        Response.Write(strJson);
32    }
33}
使用物件初始化方式塞值看起來直覺多了
不過為了物件序列化還要特地宣告類別,這…倒不如使用Linq吧

5.用Linq+匿名物件寫法 直接組JSON
這招在Json.net的官方文件有範例(用JObject.FromObject的那個):http://james.newtonking.com/projects/json/help/html/CreatingLINQtoJSON.htm
但只有範例,沒寫為什麼Linq要那樣寫,誰看得懂阿XD
要用Linq直接組JSON
大概把握幾點:
Json Object:Json字串用大括號{}表示,Linq也是用大括號{}表示
Json Object的Name(Key、Property):Json字串在兩個雙引號””裡寫一個英文單字,Linq就直接寫英文單字即可
Json Array:Json字串用中括號[]表示,Linq就用from o in XXX select o,這種回傳IEnumerable的寫法(如果JSON字串是物件陣列的話就用from o in XXX select new {欄位1=YYY}這種形式)
Json Value:Linq就看Json字串填什麼值就跟著填什麼值
01protected void Page_Load(object sender, EventArgs e)
02{
03    if (!IsPostBack)//Get Method
04    {
05        //準備資料
06        List<string> registration_ids = new List<string>() { "4""8""15""16""23""42" };
07 
08        //用Linq直接組
09        var result = new
10                        {
11                            collapse_key = "score_update",
12                            time_to_live = 108,
13                            delay_while_idle = true,
14                            data = new{
15                                score ="4x8",
16                                time = "15:16.2342"
17                            },
18                            registration_ids = from s in registration_ids
19                                                        select s
20 
21                        };
22 
23        //序列化為JSON字串並輸出結果
24        Response.Write(JsonConvert.SerializeObject(result));
25    }
26}
請看圖示解說↓
image
由於Linq支援DataTable的查詢,所以再難的JSON格式都組得出來,不用再跑for迴圈寫一堆Code,開發速度大幅提昇
以下截自實務上的一段Code(有做了一點修改)
01DataTable dt = new DataTable();//撈出一張資料表的所有數據(這個表類似北風資料庫的員工資料表,有階層關係)
02DataTable dtDetail = new DataTable();//上一張表的一對多明細表
03var firstLevel = dt.Select("pid is NULL");//第一層數據
04var result = new
05{
06      Info = new
07    {
08        Level1 = from a in firstLevel
09                 join b in dt.AsEnumerable() on a.Field<int>("id") equals b.Field<int?>("pid") into secondLevel
10                 select new
11                 {
12                     Title =  a.Field<string>("Title"),
13                     Level2 = secondLevel.Select(c => new
14                     {
15                         Title =  c.Field<string>("Title"),
16                         Photos = from s in secondLevel
17                                       join uu in dtDetail.AsEnumerable() on s.Field<int>("id") equals uu.Field<int>("id")
18                                       where s.Field<int>("id") == c.Field<int>("id")
19                                select new
20                                {
21                                    PhotoID = uu.Field<string>("PhotoID"),
22                                    PhotoTitle = uu.Field<string>("PhotoTitle")
23                                }
24 
25                     })
26                 }
27    }
28};
※不過要注意對於寫的人是寫很快,後人維護會有閱讀困難的可能


最後附上一張比較圖
image