web-dev-qa-db-ja.com

JSONオブジェクトの__typeプロパティをシリアル化しない方法

WebMethodScriptServiceから返されるすべてのオブジェクトは、dという名前のプロパティのデータを持つJSONオブジェクトにラップされます。それで大丈夫です。しかし、追加の__typeプロパティは、jQueryを使用して手動処理を行うため、クライアントに提供されます。

出来ますか?

62
Robert

クラスのデフォルトコンストラクタを作成して、webmethodがpublic以外を返すようにすると、___type:ClassName_部分がシリアル化されないことがわかりました。

デフォルトのコンストラクタprotected internal ClassName() { }を宣言することもできます

38
John Morrison

返される型は別のDLLにあるため、Johnのソリューションは機能しませんでした。 DLLを完全に制御できますが、コンストラクターが内部の場合、戻り値の型を構築できません。

戻り値の型がライブラリ内のパブリック型であることが原因なのかどうか疑問に思います-私は多くのAjaxを実行してきましたが、これはこれまで見たことがありません。

クイックテスト:

  • 戻り値の型宣言を一時的にApp_Codeに移動しました。それでも__typeシリアル化。

  • 同様に、JMごとに保護された内部コンストラクターを適用しました。これは機能しました(したがって、彼は投票権を得ます)。

奇妙なことに、__type汎用戻り型を使用する場合:

[WebMethod]
public static WebMethodReturn<IEnumerable<FleetObserverLiteAddOns.VehicleAddOnAccountStatus>> GetAccountCredits()

ただし、solutionは、DLL butWebMethodの戻り値の型をobjectに変更します。つまり、.

[WebMethod]
public static object ApplyCredits(int addonid, int[] vehicleIds) 

の代わりに

[WebMethod]
public static WebMethodReturn ApplyCredits(int addonid, int[] vehicleIds)
23
Stephen Kennedy

これらの提案のいくつかを.NET 4 WCFサービスで試してみましたが、機能しないようです-JSON応答にはまだ__typeが含まれています。

型ヒントを削除するために見つけた最も簡単な方法は、エンドポイントの動作をenableWebScriptからwebHttpに変更することです。

    <behavior name="MapData.MapDataServiceAspNetAjaxBehavior">
      <webHttp />
    </behavior>

ASP.NET AJAXクライアントを使用している場合は、デフォルトのenableWebScriptの動作が必要ですが、JavaScriptまたはjQueryを使用してJSONを操作している場合は、おそらくwebHttpの動作の方が適しています。

16
Jonathan Sayce

ServiceStack.Text JSON Serializer を使用している場合、必要なことは次のとおりです。

JsConfig.ExcludeTypeInfo = true;

この機能は自動的に v2.28 に追加されましたが、上記のコードはそれをシリアル化から除外します。 Typeでこの動作を変更することもできます:

JsConfig<Type>.ExcludeTypeInfo = true;
11
Brett Veenstra

神秘的な「__type」の根本原因を絞り込んだと思います!

問題を再現できる例を次に示します。

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class Test : System.Web.Services.WebService
{
    public class Cat
    {
        public String HairType { get; set; }
        public int MeowVolume { get; set; }
        public String Name { get; set; }
    }

    [WebMethod]
    public String MyMethodA(Cat cat)
    {
        return "return value does not matter";
    }

    [WebMethod]
    public Cat MyMethodB(String someParam)
    {
        return new Cat() { HairType = "Short", MeowVolume = 13, Name = "Felix the Cat" };
    }
}

ここが重要な部分です!

MyMethodA()が同じ.asmxファイルに存在するためです。andはクラスCatをパラメーターとして使用します。..__typeは、他のメソッドMyMethodB()の呼び出しから返されるJSONに追加されます。

それらは異なる方法ですが!!

私の理論は次のとおりです。

  1. このようなWebサービスを記述する場合、Microsoftのコードは、[WebMethod]や[ScriptService]などの正しい属性を使用したため、JSONのシリアル化/逆シリアル化の動作を自動的にフックします。
  2. この自動マジックMicrosoftコードが実行されると、Catクラスをパラメーターとして受け取るメソッドが検出されます。
  3. JSON ...からCatオブジェクトを受信するので...そう......私がeverからJSONとしてCatオブジェクトを返す場合現在のWebサービスクラスの任意のメソッド... __typeプロパティを指定するので、後でC#に逆シリアル化するときに簡単に識別できます。
  4. ニャーハハハハ...

重要な注意事項

生成されたJSONに__typeプロパティが表示されるのを防ぐには、問題のクラス(この場合はCat)をWebサービスのWebMethodsのパラメーターとして受け取らないようにします。したがって、上記のコードでは、単にMyMethodA()を変更してCatパラメーターを削除してみてください。これにより、__typeプロパティがnotに生成されます。

3
ClearCloud8

JavaScriptTypeResolverにnullを渡すと、__ typeはシリアル化されません

JavaScriptSerializer serializer = new JavaScriptSerializer(null);
string json = serializer.Serialize(foo);
3
Adam

DataContractクラスのinternalまたはprotected internalコンストラクターに関するJohn Morrisonのアドバイスに加えて、Webサービスおよび大部分のWCFで驚くほどうまく機能するため、追加のweb.configファイルを変更します。の代わりに <enableWebScript/>要素の使用<webHttp/>エンドポイントの動作Behaviors、例:

<endpointBehaviors>
  <behavior name="MyServiceEndpoint">
    <webHttp/>
  </behavior>
</endpointBehaviors>
2
Art Kg

これは良い解決策ではありませんが、 Json.net ライブラリを使用する場合、追加することで一部のプロパティを無視できます[JsonIgnore]属性。

2
Mironline

[Serializable]属性を使用しないでください。

以下はそれを行うべきです

JavaScriptSerializer ser =新しいJavaScriptSerializer(); string json = ser.Serialize(objectClass);

1
sean

これを回避する方法を次に示します

    [WebMethod]
    [ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
    public void Status()
    {
        MyObject myObject = new MyObject(); // Your class here
        var json = Newtonsoft.Json.JsonConvert.SerializeObject(myObject);

        HttpContext.Current.Response.Write(json);
    }
1
Liran Barniv

スレッドに少し遅れましたが、ここに行きます。

Json文字列に追加されるプロパティがList <T>の場合にも同じ問題が発生しました。私たちがやったのは、Tの配列である別のプロパティを追加することでした。

前。

[DataMember]
public List<Person> People { get; set; }

後。

public List<Person> People { get; set; }

[DataMember(Name = "People")]
public Person[] Persons {
    get {
        return People.ToArray();
    }
    private set { }
}

理想的なソリューションではありませんが、トリックを行います。

1
Michael

私の2セント、しかしその日の遅く:他の人が言及したように、「__ type」プロパティを防ぐための2つの方法があるようです:

a)パラメーターレスコンストラクターを保護する

b)クラスをパラメーターとしてWebメソッドに渡すことを避ける

クラスをパラメーターとして渡す必要がない場合は、コンストラクターを「内部保護」することができます。空のオブジェクトを作成する必要がある場合は、ダミーメソッドを使用してファクトリメソッドまたはその他のコンストラクタを追加します。

ただし、クラスをパラメーターとしてWebメソッドに渡す必要がある場合は、パラメーターなしのコンストラクターが保護されていると機能しないことがわかります(おそらく、渡されたjsonデータをクラスにデシリアライズできないため、ajax呼び出しは失敗します) )。

これが私の問題であったため、(a)と(b)の組み合わせを使用する必要がありました。パラメーターなしのコンストラクターを保護し、Webメソッドへのパラメーター専用に使用されるダミー派生クラスを作成します。例えば:

public class MyClass
{
    protected internal MyClass() { }
    public MyClass(Object someParameter) { }
    ...
}

// Use this class when we need to pass a JSON object into a web method
public class MyClassForParams : MyClass
{
    public MyClassForParams() : base() { }
}

MyClassを取り込む必要があるWebメソッドは、代わりにMyClassForParamsを使用します。

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public MyClass DoSomething(MyClassForParams someObject)
{
    // Do something with someObject
    ...
    // Maybe return a MyClass object
    ...
}
1
Etherman

これで解決するはずです。

System.WebExtensions.dllのJavaScriptSerializerのプライベートSerializeValueメソッドでは、__ typeが解決できる場合、内部辞書に追加されます。

リフレクターから:

private void SerializeValue(object o, StringBuilder sb, int depth, Hashtable objectsInUse)
{
    if (++depth > this._recursionLimit)
    {
        throw new ArgumentException(AtlasWeb.JSON_DepthLimitExceeded);
    }
    JavaScriptConverter converter = null;
    if ((o != null) && this.ConverterExistsForType(o.GetType(), out converter))
    {
        IDictionary<string, object> dictionary = converter.Serialize(o, this);
        if (this.TypeResolver != null)
        {
            string str = this.TypeResolver.ResolveTypeId(o.GetType());
            if (str != null)
            {
                dictionary["__type"] = str;
            }
        }
        sb.Append(this.Serialize(dictionary));
    }
    else
    {
        this.SerializeValueInternal(o, sb, depth, objectsInUse);
    }
}

タイプを判別できない場合、シリアル化は続行されますが、タイプは無視されます。幸いなことに、匿名型はgetType()を継承し、返される名前はコンパイラによって動的に生成されるため、TypeResolverはResolveTypeIdに対してnullを返し、「__ type」属性はその後無視されます。

また、念のために内部コンストラクターでジョン・モリソンのアドバイスを受けましたが、このメソッドだけを使用しても、JSON応答で__typeプロパティを取得していました。

//Given the following class
[XmlType("T")]
public class Foo
{
    internal Foo()
    {

    }

    [XmlAttribute("p")]
    public uint Bar
    {
        get;
        set;
    }
}

[WebService(Namespace = "http://me.com/10/8")]
[System.ComponentModel.ToolboxItem(false)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MyService : System.Web.Services.WebService
{

    //Return Anonymous Type to omit the __type property from JSON serialization
    [WebMethod(EnableSession = true)]
    [System.Web.Script.Services.ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json, XmlSerializeString = false)]
    public object GetFoo(int pageId)
    {
        //Kludge, returning an anonymois type using link, prevents returning the _type attribute.
        List<Foo> foos = new List<Foo>();
        rtnFoos.Add( new Foo(){
            Bar=99
        }};

        var rtn = from g in foos.AsEnumerable()
                   select g;

        return rtn;
    }
}

注:シリアル化された型からXMLシリアル化属性を読み取り、JSONをさらに圧縮する継承されたJSON型コンバーターを使用しています。 CodeJournal に感謝します。魅力のように機能します。

0
Laramie

JavaScriptSerializerを使用した@seanの答えに加えて。

JavaScriptSerializerを使用してメソッドのResponseFormat = WebMessageFormat.Jsonをマークすると、結果の応答は二重JSONエンコードに加えて、結果の応答がstringの場合、二重引用符で区切られます。

これを回避するには、 this excellent answer のソリューションを使用して、コンテンツタイプをJSON(上書き)として定義し、JavaScriptSerializerのバイナリ結果をストリーミングします。

上記の回答のコードサンプル:

public Stream GetCurrentCart()
{
    //Code ommited
    var j = new { Content = response.Content, Display=response.Display,
                  SubTotal=response.SubTotal};
    var s = new JavaScriptSerializer();
    string jsonClient = s.Serialize(j);
    WebOperationContext.Current.OutgoingResponse.ContentType =
        "application/json; charset=utf-8";
    return new MemoryStream(Encoding.UTF8.GetBytes(jsonClient));
}

JavaScriptSerializerは、デフォルトで参照されないSystem.Web.Script.SerializationにあるSystem.Web.Extensions.dll名前空間にあります。

0
Alex Pandrea