code

JSON 객체에서 __type 속성을 직렬화하지 않는 방법

codestyles 2020. 11. 28. 09:30
반응형

JSON 객체에서 __type 속성을 직렬화하지 않는 방법


A로부터 모든 객체의 I 리턴 WebMethod(A)의이 ScriptService라는 이름의 속성 데이터와 JSON 객체로 싸여있다 d. 괜찮아. 그러나 __typejQuery로 수동 처리를 수행하기 때문에 추가 속성이 클라이언트에 제공되는 것을 원하지 않습니다 .

가능합니까?


내 웹 메서드가 public 이외의 것을 반환하는 클래스의 기본 생성자를 만들면 해당 __type:ClassName부분을 직렬화하지 않습니다 .

기본 생성자를 선언 할 수 있습니다. protected internal ClassName() { }


내가 반환하는 유형이 별도의 DLL에 있기 때문에 John의 솔루션이 나를 위해 작동하지 않았습니다. 해당 DLL에 대한 모든 권한이 있지만 생성자가 내부 인 경우 반환 형식을 생성 할 수 없습니다.

나는 반환 유형이 라이브러리의 공개 유형이 원인일지도 모른다고 생각했다. 나는 많은 Ajax를 해왔지만 전에는 본 적이 없었다.

빠른 테스트 :

  • 반환 유형 선언을 일시적으로 App_Code로 이동했습니다. 여전히 __type직렬화됩니다.

  • 동일하게 보호 된 내부 생성자를 JM에 적용했습니다. 이것은 효과가있었습니다 (그래서 그는 투표를 얻습니다).

이상하게도 __type일반 반환 유형을 얻지 못합니다 .

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

솔루션 나를 위해, 그러나, DLL에서 내 반환 형식을두고 있지만 것이었다 객체에의 WebMethod 반환 형식을 변경 , 즉

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

대신에

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

.NET 4 WCF 서비스로 이러한 제안 중 일부를 시도했지만 작동하지 않는 것 같습니다. JSON 응답에는 여전히 __type이 포함되어 있습니다.

유형 힌트를 제거하는 가장 쉬운 방법은 엔드 포인트 동작을 enableWebScript에서 webHttp로 변경하는 것입니다.

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

ASP.NET AJAX 클라이언트를 사용하는 경우 기본 enableWebScript 동작이 필요하지만 JavaScript 또는 jQuery로 JSON을 조작하는 경우 webHttp 동작이 더 나은 선택 일 수 있습니다.


ServiceStack.Text JSON Serializer사용하는 경우 다음을 수행하면 됩니다.

JsConfig.ExcludeTypeInfo = true;

이 기능은 v2.28 에서 자동으로 다시 추가 되었지만 위의 코드는 직렬화를 방지합니다. 다음을 사용하여이 동작을 변경할 수도 있습니다 Type.

JsConfig<Type>.ExcludeTypeInfo = true;

JavaScriptTypeResolver에 대해 null을 전달하면 __type이 직렬화되지 않습니다.

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

수수께끼가 나오는 "__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 파일에 존재 이유만으로 매개 변수로 클래스 고양이 걸리는 JSON은 다른 메소드를 호출에서 반환에 .... __type이 추가됩니다 MyMethodB를 ().

방법은 다르지만 !!

내 이론은 다음과 같습니다.

  1. 이와 같은 웹 서비스를 작성할 때 Microsoft의 코드는 [WebMethod] 및 [ScriptService]와 같은 올바른 속성을 사용했기 때문에 JSON 직렬화 / 역 직렬화 동작을 자동으로 연결합니다.
  2. 이 자동 마법 Microsoft 코드가 실행되면 Cat 클래스를 매개 변수로 사용하는 메서드를 찾습니다.
  3. 그것은 수치 ... 오 ... 좋아 .... JSON에서 Cat 개체를받을 것이기 때문에 ... 그러므로 ... 현재 웹 서비스의 어떤 메서드에서든 Cat 개체를 JSON 으로 반환하면 class ... 나중에 C #으로 역 직렬화 할 때 쉽게 식별 할 수 있도록 __type 속성을 제공합니다.
  4. 냐하 하하하 ...

중요 사항

웹 서비스의 WebMethods에 대한 매개 변수로 문제의 클래스 (제 경우에는 Cat)를 사용하지 않도록하여 생성 된 JSON에 __type 속성이 표시되지 않도록 할 수 있습니다. 따라서 위의 코드에서 MyMethodA ()를 수정하여 Cat 매개 변수를 제거하십시오. 이로 인해 __type 속성 이 생성 되지 않습니다 .


이것이 좋은 해결책인지는 모르겠지만 Json.net 라이브러리 를 사용하는 경우 [JsonIgnore] 속성 을 추가하여 일부 속성을 무시할 수 있습니다 .


웹 서비스 및 대부분의 WCF에 대해 놀랍도록 잘 작동하는 DataContract 클래스의 내부 또는 보호 된 내부 생성자에 대한 John Morrison의 조언 외에도 web.config 파일을 추가로 변경해야 할 수 있습니다. endpointBehaviors에 대한 <enableWebScript/>요소 사용 대신 <webHttp/>, 예 :

<endpointBehaviors>
  <behavior name="MyServiceEndpoint">
    <webHttp/>
  </behavior>
</endpointBehaviors>

[Serializable] 속성을 사용하지 마십시오.

다음은 그것을해야합니다

JavaScriptSerializer ser = new JavaScriptSerializer (); 문자열 json = ser.Serialize (objectClass);


스레드에 조금 늦었지만 여기에 있습니다.

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 { }
}

이상적인 솔루션은 아니지만 트릭을 수행합니다.


내 2 센트, 그러나 늦은 시간 : 다른 사람들이 언급했듯이 "__type"속성을 방지하는 두 가지 방법이있는 것 같습니다.

a) 매개 변수없는 생성자 보호

b) 클래스를 매개 변수로 웹 메소드에 전달하지 마십시오.

클래스를 매개 변수로 전달할 필요가없는 경우 생성자를 "내부 보호"로 만들 수 있습니다. 빈 개체를 만들어야하는 경우 팩토리 메서드 또는 더미 매개 변수가있는 다른 생성자를 추가합니다.

그러나 클래스를 매개 변수로 웹 메소드에 전달해야하는 경우 매개 변수없는 생성자가 보호되면 작동하지 않음을 알 수 있습니다 (아약스 호출이 실패합니다. 아마도 json 데이터로 전달 된 것을 클래스로 역 직렬화 할 수 없기 때문일 것입니다) ).

이것이 내 문제 였기 때문에 (a)와 (b)의 조합을 사용해야했습니다. 매개 변수없는 생성자를 보호하고 웹 메서드에 대한 매개 변수에 독점적으로 사용할 더미 파생 클래스를 만들어야했습니다. 예 :

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를 가져와야하는 웹 메서드는 대신 MyClassForParams를 사용합니다.

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

여기에 방법이 있습니다.

    [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);
    }

이것은 그것을 해결할 것입니다.

System.WebExtensions.dll에있는 JavaScriptSerializer의 private 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);
    }
}

If the type can't be determined, serialization will still proceed, but the type will be ignored. The good news is that since anonymous types inherit getType() and the names returned are dynamically generated by the compiler, the TypeResolver returns null for ResolveTypeId and the "__type" attribute is subsequently ignored.

I also took John Morrison's advice with the internal constructor just in case, though using just this method, I was still getting __type properties in my JSON response.

//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;
    }
}

Note: I'm using an inherited JSON type converter that reads the XML Serialization attributes from serialized types to further compress the JSON. With thanks to CodeJournal. Works like a charm.


In addition to @sean 's answer of using JavaScriptSerializer .

When using JavaScriptSerializer and marking the method's ResponseFormat = WebMessageFormat.Json, the resulting response has double JSON encoding plus that if the resulting response is string, it will be plced bweteen double quotes.

To avoid this use the solution from this excellent answer to define the content type as JSON (overwrite) and stream the binary result of the JavaScriptSerializer.

The code sample from the mentioned answer:

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 is in the System.Web.Script.Serialization namespace found in System.Web.Extensions.dll which is not referenced by default.


var settings = new DataContractJsonSerializerSettings();
settings.EmitTypeInformation = EmitTypeInformation.Never;
DataContractJsonSerializer serializerInput = new DataContractJsonSerializer(typeof(Person), settings);
var ms = new MemoryStream();
serializerInput.WriteObject(ms, personObj);
string newRequest = Encoding.UTF8.GetString(ms.ToArray());

This is a bit of a hack, but this worked for me (using C#):

s = (JSON string with "__type":"clsname", attributes)
string match = "\"__type\":\"([^\\\"]|\\.)*\",";
RegEx regex = new Regex(match, RegexOptions.Singleline);
string cleaned = regex.Replace(s, "");

Works with both [DataContract] and [DataContract(Namespace="")]

참고URL : https://stackoverflow.com/questions/627356/how-to-not-serialize-the-type-property-on-json-objects

반응형