2014-10-06 4 views
1

많은 공유 속성이있는 개체를 반환하는 서비스를 만들려고 시도하지만 일부 상황에서는 속성 하나가 매우 제한적이어야합니다. 이로 인해 속성 이름이 직렬화 된 출력에서 ​​재사용되어 브라우저에서 잘못된 동작이 발생하는 이상하고 바람직하지 않은 동작이 발생합니다.하위 클래스에서 "new"를 사용하면 JavascriptSerializer가 두 번 serialize됩니다.

void Main() 
{ 
    System.Web.Script.Serialization.JavaScriptSerializer json = new System.Web.Script.Serialization.JavaScriptSerializer(); 

    json.Serialize(new Full(true)).Dump(); 
    json.Serialize(new Limited()).Dump(); 
} 

public class Full 
{ 
    public String Stuff { get { return "Common things"; } } 
    public FullStatus Status { get; set; } 

    public Full(bool includestatus) 
    { 
     if(includestatus) 
      Status = new FullStatus(); 
    } 
} 

public class Limited : Full 
{ 
    public new LimitedStatus Status { get; set; } 

    public Limited() : base(false) 
    { 
     Status = new LimitedStatus(); 
    } 
} 

public class FullStatus 
{ 
    public String Text { get { return "Loads and loads and loads of things"; } } 
} 

public class LimitedStatus 
{ 
    public String Text { get { return "A few things"; } } 
} 

이 인쇄 : 여기에 문제를 볼 수있는 (당신이 System.Web.Extensions에 대한 참조를 추가하는 경우) LINQPad에 붙여 넣을 수있는 예입니다 JSON.parse가 브라우저에서 호출

{"Stuff":"Common things","Status":{"Text":"Loads and loads and loads of things"}} 
{"Status":{"Text":"A few things"},"Stuff":"Common things","Status":null} 

두 번째 상태는 첫 번째 상태를 재정의합니다. 즉, 상태는 항상 null입니다.

이 문제를 해결하는 유일한 방법은 refactor를 사용하는 것입니다. 따라서 FullStatus와 LimitedStatus는 공통 부모로부터 상속되며 override이 아닌 new 비트가 실제 코드에서는 더 복잡하지만 가능합니다. 내 가정이 맞습니까? 또한 이것이 예상되는 동작인지 또는 버그인지 알고 싶습니다.

답변

1

예, 귀하의 가정은 정확합니다. new 키워드는 override과 같지 않습니다. 단지 기본 클래스 속성을 "숨기"만하지만 원래 속성은 여전히 ​​그대로이며 반사를 통해 여전히 발견 될 수 있습니다 (직렬화자가 작업하는 방식입니다).

일반적으로 기본 클래스의 메서드 나 속성을 정의한 다음 기본 클래스의 기능을 제거하는 파생 클래스의 다른 메서드 나 속성으로 바꾸는 것이 "code smell"으로 간주됩니다. 이는 Liskov Substitution Principle을 위반합니다.

Full에서 Limited을 유도하는 대신에, 나는 당신이 그들을위한 추상 기본 클래스를 만들고 거기에 공통적 인 것들을 넣으라고 제안 할 것입니다. 그런 다음 각 하위 클래스 (즉, 회원님의 다른 유형의 Status 회원)과 다른 또는 독점적 인 항목을 추가 할 수 있습니다. 예를 들어

:

class Program 
{ 
    static void Main(string[] args) 
    { 
     System.Web.Script.Serialization.JavaScriptSerializer json = 
      new System.Web.Script.Serialization.JavaScriptSerializer(); 

     Console.WriteLine(json.Serialize(new Full(true))); 
     Console.WriteLine(json.Serialize(new Limited())); 
    } 
} 

public abstract class Base 
{ 
    public String Stuff { get { return "Common things"; } } 
} 

public class Full : Base 
{ 
    public FullStatus Status { get; set; } 

    public Full(bool includestatus) 
    { 
     if (includestatus) 
      Status = new FullStatus(); 
    } 
} 

public class Limited : Base 
{ 
    public LimitedStatus Status { get; set; } 

    public Limited() 
    { 
     Status = new LimitedStatus(); 
    } 
} 

public class FullStatus 
{ 
    public String Text { get { return "Loads and loads and loads of things"; } } 
} 

public class LimitedStatus 
{ 
    public String Text { get { return "A few things"; } } 
} 

출력 : 링크에 대한

{"Status":{"Text":"Loads and loads and loads of things"},"Stuff":"Common things"} 
{"Status":{"Text":"A few things"},"Stuff":"Common things"} 
+0

감사합니다! 당신의 대답을 내가 내려간 길인 것처럼 정확하게 표시했습니다 - 처음부터 빨리 뭔가를 해킹하려고 시도해 줬습니다. 난 여전히 serializers 동작 뒤에 이유에 관심이있을거야,하지만 난 JSON 완벽하게 유효합니다 (글쎄, 적어도 크롬 - 이전 속성은 조용히 무시됩니다)로 추측 그것은 serializer에서 버그가 아니에요, 그냥 뭔가 프로그래머가 자신의 모델을 수정해야합니다. – Whelkaholism

+0

왜 serializer가 동일한 객체에 같은 이름의 두 속성을 출력합니까? 일반적으로 그렇지는 않지만 "새"키워드를 사용하여 속성을 숨길 경우 직렬화 기가 객체를 통해 반영 될 때 동일한 이름을 가진 두 개의 속성을 찾습니다. 그러면 쓰기시 해당 상황을 검사하지 않습니다 JSON. JSON의 동일한 객체에 같은 이름의 두 속성이 있으면 [spec] (http://tools.ietf.org/html/rfc7159)에 따라 기술적으로 올바르지 않지만 JSON의 상호 운용성이 떨어집니다. (계속) –

+0

역 직렬화 중에 Json.Net 자체를 포함한 일부 파서는 역설적으로 중복 된 속성 이름을 포함하는 객체를 처리 할 수 ​​없습니다. 이러한 이유로 중복 된 속성 이름을 사용하여 JSON을 만드는 것은 나쁜 형식으로 간주되며 가능하면 사용하지 않는 것이 가장 좋습니다. –