QA@IT

JSON.NETのJSONシリアライズ出力で、二重引用符が付いてしまう。

1306 PV

VisualStudio2015(C#)でSQLServer2012から取得したデータをJSON.NETでJSON出力するWebApiを作っています。

https://qiita.com/octopa0327/items/46a15d7d463233605cd0
この辺りの記事を参考にしていますが、
出力されたJSONファイル内の文字列全体が二重引用符で括られてしまい、さらに文字列内に二重引用符があった場合、
その前に「\(エスケープバックスラッシュまたは¥マーク)」が入ってしまっております。
これらを入らないようにするにはどのような設定が必要なのでしょうか。

現在のコードは以下です。
■コード

    //Contents.cs
    [Table("t_Contents")]
    public partial class Content
    {
        [Key]
        public int ContentId { get; set; }
        public string ContentName { get; set; }
    }

    //ContentsController.cs
    using Newtonsoft.Json;
    public class ContentsController : ApiController
    {
        private ContentsContext db = new ContentsContext();    //DBコンテキストは定義済みです。
        public string GetContents()
        {
            string json = JsonConvert.SerializeObject(db.Contents);    //db.Contentsのデータは取得できています。
            return json;
        }
    }

■SQLServer内データ(t_Contentsテーブル)
ContentId ContentName
1 テスト動画1
2 テスト画像1
3 テスト動画2

■現在の結果JSON

"[{\"ContentId\":1,\"ContentName\":\"テスト動画1\"},{\"ContentId\":2,\"ContentName\":\"テスト画像1\"},{\"ContentId\":3,\"ContentName\":\"テスト動画2\"}]"

↑この結果文字列の、最初と最後の"と、文字列中の\"ContentId\部分が挿入されてしまい、
JSONの書式になっていないようです。
検索した限りでは、どのサンプルにも入っていませんでした。

なお、「GetContents()」メソッドの戻り値をstringではなく、「IQueryable」にし、

    //Contents.cs
    public IQueryable<Content> GetContents()
    {
        return db.Contents;
    }

とすると以下のように期待する形になります。

[
{
"ContentId": 1,
"ContentName": "テスト動画1"
},
{
"ContentId": 2,
"ContentName": "テスト画像1"
},
{
"ContentId": 3,
"ContentName": "テスト動画2"
}
]

(項目の並び等を調整できない為、今回はJSON.NETの使用を考えています。)

宜しくお願い致します。

回答

↑この結果文字列の、最初と最後の"と、文字列中の\"ContentIdの\部分が挿入されてしまい、

それは単純に C# の文字列として " が \" にエスケープされているだけのようですが?

エスケープを解除すると、

[{"ContentId":1,"ContentName":"テスト動画1"},{"ContentId":2,"ContentName":"テスト画像1"},{"ContentId":3,"ContentName":"テスト動画2"}]

という正しい JSON 文字列になって、それを C# のオブジェクトにデシリアライズすると List<Content> 型になるはずです。

編集 履歴 (0)
  • その形にしたいのですが、世のapiサンプルや、サイトで実際に使われているjsonの仕組みを見ると、
    そもそもエスケープがされない状態で出力されていた為、シリアライズ側でエスケープしない方法があるのかなと思いました。
    (unicodeエスケープされているサイト等は見かけましたが。)

    マイクロソフトの文献などでもこのように先頭と最後が`"`で括られているjsonのやり取りが見つからないのです
    -
  • すみません。200文字までしか入らないんですね。
    以下、上記コメントの続きです。
    ↓↓↓↓↓↓↓↓↓
    ~が見つからないのですが、記事などご存知でしょうか。
    そもそも`db.Contents`をそのままシリアライズするのではなく、中のデータを元に手作りするのが主流なのでしょうか。
    -
  • 質問欄に書いてある「現在の結果JSON」はどのようにして見ているのでしょうか? ひょっとして Visual Studio のデバッガで rerun json; あたりにブレークポイントを置いて json を見ているとか? -
  • Visual Studioで実行して起動したブラウザにapiのURLを入れて実行すると、jsonファイルとしてダウンロードされました。
    その中身をterapadのようなテキストエディタで見ています。
    -
  • その前に、ASP.NET Web API の話ですよね? 何故コードを書いて自力でシリアライズしているのですか? 単純に、return db.Content.ToList(); として望む結果は得られませんか?(未検証ですので違ったらすみません) -
  • 質問者さんのコード return json; の json は string 型ですがそれを JSON にシリアライズするから期待した結果にならないのでしょうか? これも未検証ですが、後で試してみます。 -
  • はい。ASP.NET Web APIです。確か仰るやり方で不要なエスケープはなくなりましたが、この出し方だとJSON出力時の項目順や、不要な項目などの設定ができませんでした。(恐らくJSON用のViewModelを作れば良いのかもしれませんが。)
    その為、融通がききそうなJSON.NETで検証しておりました。
    -
  • 上の私のコメント「return json; の json は string 型ですがそれを JSON にシリアライズするから期待した結果にならない」のことを検証してみましたが、当たりでした。質問者さんと同じ結果になります。文字列を JSON にシリアライズするので、文字列に含まれる " は \" にエスケープされるという、考えてみれば当たり前の結果でした。 -
  • 一旦 List<Content> list = db.Contents.ToList(); とし、「項目順や、不要な項目などの設定」は list を細工するようにして、細工後に return list; とすることで望むことはできませんか? -
  • ちょっと理解できていないのですが、
    「文字列をJSONにシリアライズ」はどこで処理されているのでしょうか。明示的にはJsonConvert.SerializeObjectでしかシリアライズしていないように見えるのですが。
    リクエストの仕方が問題なのでしょうか。
    -
  • ありがとうございます。
    ひとまず、List<Content> list = db.Contents.ToList(); で望むJSONが出せるか試してみます。
    サンプルのようにConsole.WriteLine等、直接文字列を出力するのと違い、returnで出力すると内部変換されてしまうんですかねぇ。。。
    -
  • >「文字列をJSONにシリアライズ」はどこで処理されているのでしょうか。

    ASP.NET Web API なので return <object>; で自動的に object を JSON 文字列にシリアライズして返します。質問者さんの例では object は string 型なので前後に " がついて、中身の " は \" にエスケープされます。
    -
  • なるほど!ASP.NET Web APIの仕様なんですね。ということはJSON.NETはASP.NET Web APIでは出番がなさそうですね。
    ありがとうございました!
    -

削除しました。

編集 履歴 (1)
  • 質問者さんは回答欄に書かないでください。私の回答に対するコメントなら私の回答欄の下の「コメントを書く」をクリックしてそこに書いてください。ここではそういう流儀のようですので、質問者さんに回答欄に書かれると訳が分からなくなってしまいますので。今からでも遅くないのでそのように修正ください。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。