3

私はこのようなタイプを持っています:

class Foo<T>
{
  public string Text { get; set; }

  public T Nested { get; set; }

  public static string ToJson(Foo<T> foo) { [...] }
}

ToJsonFoo<Bar>微調整では実現不可能な方法で、インスタンスを JSON にシリアライズしますJsConfig。また、ToJsonServiceStack.Text に依存してシリアライズします。Nestedこれは のインスタンスになる可能性がありますFoo<Baz>

残念ながら、JsConfig実装されている方法は、 forおよび other forJsConfig<T>の静的変数のセットがあることを意味します。また、AFAIK、ServiceStack.Text は、オープン ジェネリック型 (つまり、のようなもの) の JSON シリアル化を構成する方法を提供しません。この静的コンストラクターを作成して、この問題を解決しようとしました:Foo<Bar>Foo<Baz>JsConfig.Add(typeof(Foo<>), config)Foo<T>

static Foo() {
  JsConfig<Foo<T>>.RawSerializeFn = ToJson;
}

これは常に機能するわけではありません。これは、静的コンストラクターがランタイムによって呼び出される順序によって異なります。どうやら、 ServiceStack.Text はシリアライザー関数をキャッシュし、API で操作が呼び出される順序に応じて、静的コンストラクターが呼び出される前にそれを実行することがあります。

var outer = new Foo<Baz> { Text = "text" };
outer.ToJson(); // OK, because Nested is null

var inner = new Foo<Bar>();
inner.ToJson(); // OK, because JsConfig<Foo<Bar>>.RawSerializeFn is Foo<T>.ToJson

outer.Nested = inner;
outer.ToJson(); // NOT OK, because SS.Text uses the default serializer for Foo<T>, not Foo<T>.ToJson

JsConfig<Foo<T>>T は実質的に任意の型 (他のジェネリック型であっても) になる可能性があるため、事前にすべてのシリアライザーを設定することはできません。

ServiceStack.Text でオープン ジェネリック型 (ネストできる) のカスタム シリアル化ルーチンを定義することは可能ですか?

4

1 に答える 1

2

ラッパーとカスタムデシリアライザーを使用して、独自の方法でこれを解決しました。すべての抽象型の基本型を作成しました。その基本型は、それがどのであるかをシステムに伝えます。

public class SetSettingItemBase
{
    public string Key { get; set; }
    public string ValueType { get; set; }
}

したがって、基本は基本的にメタデータ (設定キー + 値の型) です。次に、オブジェクト DTO は、実際の値を追加して単純に拡張します。

public class SetSettingItem : SetSettingItemBase
{
    public object Value { get; set; }
}

ただのobject. これは DTO であり、実際のオブジェクトではありません。後でキャストするか、シリアル化後に実数型またはジェネリック型に変換できます。

私のカスタムシリアライゼーションは次のとおりです。

JsConfig<SetSettingItem>.RawDeserializeFn = str =>
            {
                var baseSettings = str.FromJson<SetSettingItemBase>();
                var ret = baseSettings.MapTo<SetSettingItem>();

                if(true) // actual condition removed here... unimportant to the example
                {
                    var dataType = Constants.KnownSettingsTypes[baseSettings.ValueType];

                    var method = typeof(JsonExtensions).GetMethod("JsonTo").MakeGenericMethod(dataType);

                    var key = "Value";
                    var parsed = JsonObject.Parse(str);

                    if(parsed.Object(key) == null)
                        key = "value";

                    ret.Value = method.Invoke(null, new object[] { parsed, key });
                }
                return ret;
            };

このメソッドは、最初に単純なベースに逆シリアル化します。そのためValue、デシリアライズ時に DTO から渡された は無視されbaseSettingsます。MapTo次に、実際のSetSettingItemDTO を準備するために呼び出します。MapToは単なるラッパーAutoMapperです。ここでは、SS の組み込みマッパーを簡単に使用できます。

セキュリティのために、設定として許可するタイプのセット リストがあります。例:

KnownSettingsTypes.Add("string", typeof(string));
KnownSettingsTypes.Add("int", typeof(int));
KnownSettingsTypes.Add("nullableint", typeof(int?));
KnownSettingsTypes.Add("nullablepercentage", typeof(double?));
KnownSettingsTypes.Add("feegrid", typeof(FeeGrid));

その後、リフレクションを使用して JsonTo メソッドを取得し、ジェネリック型パラメーターをKnownSettingsTypes辞書から動的に渡します。

最後に、ジェネリック メソッドを使用してオブジェクトを解析し、(大文字と小文字の区別に応じて) orJsonObject.Parseを探し、以前に作成したダイナミックを使用して明示的に変換します。ValuevalueJsonObjectmethod

最終的な結果として、ここで DTO の一部としてすべての異なるタイプの設定を渡すことができます。

これは当分の間私の目的を果たしましたが、例を見ると、次の 2 つの点で改善されていることがわかりました。

  1. 解析後、 mySetSettingItemを に変換SettingItem<T>できるので、コード内で厳密に型指定されたオブジェクトとして使用できます。この例は、DTO がネットワーク経由で取得するためのものであることを忘れないでください。
  2. type私がチェックするために人に渡すように要求する代わりに、設定Keyをチェックして、それがどのタイプであると想定されているかを知り、それに応じて解析することができます。私の例では、設定とそのタイプのマスター リストを確認してもtype、念のために渡して、一致しない場合は例外をスローする必要があるでしょう。
于 2014-05-20T13:11:28.207 に答える