8

MVC 4の新しいシングルページアプリケーションツールを試してみたところ、私が見つけた例のいずれにも、WebApiを介して更新されたDateTimeの例が含まれていないことに気付きました。私はすぐにその理由を知りました。

提供されたテンプレートから標準SPAを生成することから始めました。次に、TodoItem.csを開き、DateTimeフィールドを追加しました。次に、コメントの指示に従ってコントローラーを生成しました。(日時フィールドがない場合、すべてが正常に機能します)。

すべてが生成された後、アプリを起動し、コントローラーインデックスに移動しました(コントローラーを「タスク」と呼びました)。期待どおりに0レコードのグリッドページを取得し、追加ボタンをクリックしました。期待どおりに編集ページに移動し、光沢のある新しい日時フィールドに日付を含むいくつかのデータを入力しました。次に、[保存]をクリックしました。

次のようなエラーが発生しました。

サーバーエラー:HTTPステータスコード:500、メッセージ:タイプSystem.Web.Http.Data.ChangeSetEntry[]のオブジェクトの逆シリアル化中にエラーが発生しました。DateTimeコンテンツ「01/01/2012」は、JSONで必要な「/ Date(」で始まり、「)/」で終わるわけではありません。

ツールはまだDateTimeをサポートしていないようです。私はそれを理解して機能させるために少し時間を費やすことができると確信していますが、すでにこの問題を修正して洞察を提供できる誰かとここで少し運が見つかるかもしれないと思いました。

誰かがすでにこれと戦った?

更新:これを尋ねてから見つけた情報をさらに追加しています。以下に示すように、フォーマッターとしてJSON.Netを使用してみました。それが最終的な解決策になると思いますが、以下のポスターが推奨するようにするだけでは十分ではありません。

JSON.Netシリアライザーを使用すると、次のエラーが発生します。

このDataControllerは、エンティティ「JObject」の操作「Update」をサポートしていません。

その理由は、JSON.Netが、フォーマッターが脱セラリゼーションしようとしているオブジェクト(System.Web.Http.Data.ChangeSet)に完全にデータを入力しないためです。

送信されるjsonは次のとおりです。

[{"Id":"0",
  "Operation":2,
  "Entity":
    {"__type":"TodoItem:#SPADateProblem.Models",
     "CreatedDate":"/Date(1325397600000-0600)/",
     "IsDone":false,
     "Title":"Blah",
     "TodoItemId":1},
  "OriginalEntity":
    {"__type":"TodoItem:#SPADateProblem.Models",
     "CreatedDate":"/Date(1325397600000-0600)/",
     "IsDone":false,
     "Title":"Blah",
     "TodoItemId":1}
}]

組み込みのJsonFormatterは、このJsonをChangeSetオブジェクトに再構成し、EntityフィールドとOriginalEntityフィールドにTodoItemオブジェクトを埋め込むことができます。

誰かがこれを適切に逆シリアル化するためにJSON.Netを入手しましたか?

4

4 に答える 4

3

問題は、現在のベータ版では、ASP.NET Web API が を使用しDataContractJsonSerializerており、これには のシリアル化に関する既知の問題がありDateTimeます。これは、この問題について Microsoft Connect で最近発生した静かなバグです。MS は、問題を追跡するバグが既にあると回答していますが、.Net 4.5/VS11 の時間枠では修正されません。

幸いなことに、 James Newton-King の優れたJSON.Netなど、別の JSON シリアライザーに置き換えることができます。

ASP.NET チームの Henrik Nielsen は、ASP.NET Web API で JSON.Net を使用する方法を示す優れたブログ投稿を行っています。これは、JSON.Net を使用する の彼の実装ですMediaTypeFormatter(ASP.NET Web API 構成にも接続する必要があります。Henrik のブログでもそれが示されています)。

public class JsonNetFormatter : MediaTypeFormatter
{
    private readonly JsonSerializerSettings settings;

    public JsonNetFormatter(JsonSerializerSettings settings = null)
    {
        this.settings = settings ?? new JsonSerializerSettings();

        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
        Encoding = new UTF8Encoding(false, true);
    }

    protected override bool CanReadType(Type type)
    {
        return type != typeof(IKeyValueModel);
    }

    protected override bool CanWriteType(Type type)
    {
        return true;
    }

    protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
    {
        var ser = JsonSerializer.Create(settings);

        return Task.Factory.StartNew(() => {
            using (var strdr = new StreamReader(stream))
            using (var jtr = new JsonTextReader(strdr))
            {
                var deserialized = ser.Deserialize(jtr, type);
                return deserialized;
            }
        });
    }

    protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, TransportContext transportContext)
    {
         JsonSerializer ser = JsonSerializer.Create(settings);

         return Task.Factory.StartNew(() =>
         {
              using (JsonTextWriter w = new JsonTextWriter(new StreamWriter(stream, Encoding)) { CloseOutput = false})
              {
                   ser.Serialize(w, value);
                   w.Flush();
              }
         });
    }
}    
于 2012-02-28T00:02:43.037 に答える
1

私はまったく同じ問題を抱えていました。json.netを機能させるために多くの時間を費やしました。私はついに、サンプルプロジェクトでTodoItemsViewModel.jsに固執するこの回避策を思いつきました。

    self.IsDone = ko.observable(data.IsDone);
    self.EnterDate = ko.observable(data.EnterDate);
    self.DateJson = ko.computed({
        read: function () {
            if (self.EnterDate() != undefined) {
                 var DateObj = new Date(parseInt(self.EnterDate().replace("/Date(", "").replace(")/", ""), 10));    //.toDateString();
                var ret = DateObj.getMonth() + 1 + "/" + DateObj.getDate() + "/" + DateObj.getFullYear();
                return ret;
            }
            else {
                return self.EnterDate();
            }
        },
        write: function (value) {
            var formattedDate = "\/Date(" + Date.parse(value) + ")\/"
            self.EnterDate(formattedDate);
        }
     });
    upshot.addEntityProperties(self, entityType);

コンテキストのために、周囲のコード行が含まれています。私はこれを次のコメントで見つけました:http://blog.stevensanderson.com/2012/03/06/single-page-application-packages-and-samples/

また、_Editor.cshtmlのhtmlを変更して、「EnterDate」ではなく「DateJson」にバインドする必要があります。

これは確かに応急修理ですが、それは小さな偉業ではない作業の美徳を持っています。

于 2012-04-13T23:05:35.407 に答える
1

次のコードを追加して、JQuery カレンダー ポップアップを機能させることもできます。

サンプル MVC 4 SPA プロジェクトの TodoItemsViewModel.js の最後にこれを追加します。

    ko.bindingHandlers.datepicker = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {};
        $(element).datepicker(options);

        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();
            observable($(element).datepicker("getDate"));
        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).datepicker("destroy");
        });

    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
        current = $(element).datepicker("getDate");

        if (value - current !== 0) {
            //$(element).datepicker("setDate", value);
            $(element).val(value.toString());
        }
    }
}

ko.bindingHandlers.date = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var jsonDate = "/Date(12567120000-1000)/";
        var value = new Date(parseInt(jsonDate.substr(6)));
        var ret = value.getMonth() + 1 + "/" + value.getDate() + "/" + value.getFullYear();
        element.innerHTML = ret;
    },

    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
    }
};

これは、datepicker にバインドする _Editor.cshtml コードのようになります

    <p>
    EnterDate:
    @*<input name="EnterDate" data-bind="value: EnterDate, autovalidate: true" />
    <span class="error" data-bind="text: EnterDate.ValidationError"></span>*@
    <input name="DateJson" data-bind="datepicker: DateJson, datepickerOptions: { minDate: new Date() }" />
    <span class="error" data-bind="text: DateJson.ValidationError"></span>

</p>

また、_Grid.cshtml ページに表示される変数を「EnterDate」から「DateJson」に変更します。

于 2012-04-14T01:44:26.780 に答える
0

JSON.NET は $type を想定していますが、__type を使用してエンティティ タイプを特定し、JObject に変換します。

次のクランクでそれを回避しました

まず、JsonSerializerSettings has.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects;であることを確認してください。

次に、独自の ````JsonTextReader を記述します

public class MyJsonTextReader : JsonTextReader
{
    public MyJsonTextReader(TextReader reader)
        : base(reader)
    { }

    public override object Value
    {
        get
        {
            var o = new ActivityManager.Models.Sched_ProposedActivities();

            if (TokenType == JsonToken.PropertyName && base.Value.ToString() == "__type")
                return "$type";
            if (TokenType == JsonToken.String && Path.ToString().EndsWith(".__type"))
            {
                string s = base.Value.ToString();
                var typeName = Regex.Match(s, ":#.*").ToString().Substring(2) + "." + Regex.Match(s, "^.*:#").ToString().Replace(":#", "");

                return
                    typeName + ", ActivityManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
            }

            return base.Value;
        }
    }
}

それを使用して、````using (MyJsonTextReader jsonTextReader = new MyJsonTextReader(streamReader)) で Json を逆シリアル化します。

于 2012-04-05T18:55:13.310 に答える