2012-01-26 5 views
0

매우 못생긴 XML을 전달하는 "웹 서비스"를 사용해야합니다. 이것은 우리에 의해 개발 된 것이 아니며, 개발자들로 하여금 적절한 XML을 이해할 수있는 기회가 전혀 없습니다.XmlSerializer 특성을 사용하여이 XML을 구문 분석하려면 어떻게해야합니까?

참고로,이 웹 서비스는 요청 본문이 아닌 HTTP GET URL 매개 변수에서도 동일한 종류의 XML을 허용합니다.이 웹 서비스는 왜 나쁜 습관인지 이해하지 못합니다.

따라서, 이와 같은 XML 매핑하는 가장 빠른 방법은 무엇입니까 : 같은 클래스로

<foo id="document"> 
    <foo id="customer"> 
     <bar name="firstname" value="Joe"/> 
     <bar name="lastname" value="Smith"/> 
     <foo id="address"> 
      <bar name="city" value="New York"/> 
      <bar name="country" value="USA"/> 
     </foo> 
    </foo> 
    <bar name="somemoredata1" value="123"/> 
    <bar name="somemoredata2" value="abc"/> 
</foo> 

:

public class Document 
{ 
    public Customer Customer { get; set; } 

    public int SomeMoreData1 { get; set; } 

    public string SomeMoreData2 { get; set; } 
} 

public class Customer 
{ 
    public Address Address { get; set; } 

    public string FirstName { get; set; } 

    public string LastName { get; set; } 
} 


public class Address 
{ 
    public string City { get; set; } 

    public string Country { get; set; } 
} 

예를 사용하여. XML Serializer 속성 또는 가능한 한 적법한 코드가 필요한 다른 방법.

필자는 foobar 요소 이름을 만들었지 만 구문 분석해야하는 XML의 구조는 완전히 동일한 규칙을 기반으로합니다.

나는 물론 이러한 클래스에서 수동으로 IXmlSerializable을 구현하거나 FooBar 수업을하고 XmlSerializer 가진 사람을 사용하지만, 이러한 옵션 중 어느 것도 좋은 해결책이 될 것으로 보인다 없었다.

미리 답변 해 주셔서 감사합니다.

+1

XmlSerialiser로 처리해야합니까? XML을 사용해도 XmlReader로 파싱하고 빌드하는 것이 더 쉽다는 것을 종종 알게됩니다. 결코 미친 XML을 염두에 두지 마십시오. –

+0

XmlSerializer로 구문 분석하는 것이 가장 간단합니다. 더 낫거나 더 쉬운 해결책이 있다면 답을 써보십시오! :) 나는 그것에 대해 궁금해. – Venemo

답변

1

XML serializer 특성으로는 작업을 수행 할 수 없습니다. 지정된 특성에서 필드 이름을 가져올 수있는 방법이 없습니다.

<xsl:template match="foo"> 
    <xsl:element name="{@id}"> 
    <xsl:apply-templates/> 
    </xsl:element> 
</xsl:template> 

<xsl:template match="bar"> 
    <xsl:element name="{@name}"> 
    <xsl:value-of select="@value"/> 
    </xsl:element> 
</xsl:template> 

업데이트 : 반대로 변환에 대한 :

트릭을 할 것입니다 다음과 같은 라인을 따라 간단한 XSLT - 수동 또는 사전 프로세스 XML (아마도 보일러를 생성) 역 직렬화 할 것이다
<xsl:template match="*[count(child::text())=1]"> 
    <bar value="{text()}" name="{local-name()}"/> 
</xsl:template> 

<xsl:template match="*"> 
    <foo id="{local-name()}"> 
    <xsl:apply-templates/> 
    </foo> 
</xsl:template> 
+0

하, XSLT 아이디어가 지금까지 가장 우아합니다! 그것에 대해 한 가지 더 : 직렬화 기가 출력 한 XML을 어떻게 못생긴 형식으로 변환 할 수 있습니까? XSLT에 익숙하지 않기 때문에 묻습니다. – Venemo

+0

역변환에 xslt를 추가하고 다른 하나를 고정했습니다. –

+0

Anton 감사합니다! XSLT는 요일을 저장합니다. :) – Venemo

1

필자는 xml serializer 또는 deserializer를 직접 사용하지 않았지만 LINQ를 사용하여 XML doc을 개체로 구문 분석합니다. 수업이 매우 간단하다면 그 경로를 들여다 볼 수 있습니다.

+0

LINQ to XML을 권장하는 경우 +1입니다. 그러나 위에 붙여진 XML이 XML OP를 대표하는 것이라면 잘 형성되지 않은 것처럼 보일 수 있습니다 (예 : 루트 노드 없음). – Tim

+0

XmlSerializer에 대해 생각하는 이유는 많은 상용구 코드를 작성할 필요가 없기 때문입니다. – Venemo

1

XmlSerializer가 다른 관심사에 의해 적용되기 때문에 XmlSerializer에 대한 의견이 많으므로 다음과 같은 접근 방식이 있습니다. 문서에서 요소의 이름이 중요하지 않은 것처럼 보일 수 있으므로 파싱에서 무시됩니다. 단점도 테스트 할 수 있습니다.

private static Document ParseDocument(XmlReader xr) 
{ 
    Document doc = new Document(); 
    while(xr.Read()) 
     if(xr.NodeType == XmlNodeType.Element) 
     if(xr.GetAttribute("id") == "customer") 
      doc.Customer = ParseCustomer(xr.ReadSubtree()); 
     else 
      switch(xr.GetAttribute("name")) 
      { 
      case "somemoredata1": 
       doc.SomeMoreData1 = int.Parse(xr.GetAttribute("value")); 
       break; 
      case "somemoredata2": 
       doc.SomeMoreData2 = xr.GetAttribute("value"); 
       break; 
      } 
     //Put some validation of doc here if necessary. 
     return doc; 
} 
private static Customer ParseCustomer(XmlReader xr) 
{ 
    Customer cu = new Customer(); 
    while(xr.Read()) 
    if(xr.NodeType == XmlNodeType.Element) 
     if(xr.GetAttribute("id") == "address") 
     cu.Address = ParseAddress(xr.ReadSubtree()); 
     else 
     switch(xr.GetAttribute("name")) 
     { 
      case "firstname": 
      cu.FirstName = xr.GetAttribute("value"); 
      break; 
      case "lastname": 
      cu.LastName = xr.GetAttribute("value"); 
      break; 
     } 
    //validate here if necessary. 
    return cu; 
} 
private static Address ParseAddress(XmlReader xr) 
{ 
    Address add = new Address(); 
    while(xr.Read()) 
    if(xr.NodeType == XmlNodeType.Element) 
     switch(xr.GetAttribute("name")) 
     { 
     case "city": 
      add.City = xr.GetAttribute("value"); 
      break; 
     case "country": 
      add.Country = xr.GetAttribute("value"); 
      break; 
     } 
    return add; 
} 

그것은 정확히 꽤 (그에게 친절 XML 몹시 꽤 아니다 아니다 : 쾌적한 XML 형식으로, 즉, 구문 분석 (요소 이름에 switch와 함께 일반적으로) 키 오프 것이 주된 일이 될 것입니다 작동하지만, 그다지 좋지는 않은 경향이 있습니다). 그러나 작동하고, 같은 유형이 문서 내의 다른 위치에서 나타날 수있는 복잡한 구조에서는 하위 트리를 사용하는 것이 좋습니다.하나는 외부로부터 값을 설정하는 정적 메소드를 바꿔 클래스 불변을 보장하고 객체를 변경할 수있는 XmlReader을 취하는 컨스트럭터로 대체 할 수있다.

이 접근법은 같은 종류의 항목 (또는 몇 가지 유형의 큰 시리즈)의 큰 시리즈로 비 병렬화하려는 대용량 문서의 경우 자체적으로 발생합니다. 왜냐하면 하나는 yield이므로 그것들이 만들어지기 때문에 첫 반응에 대한 지연에 상당한 차이가있을 수 있습니다.

+0

Jon. 나는이 경우'사용하기 '를 거의 사용하지 않았다. 'XmlReader'가 메소드에 전달되었고, 호출자는 여전히 그것을 필요로 할 수 있습니다. –

+0

@ JohnSaunders가 무엇을할까요? OP에있는 문서는 끝날 때 끝까지 읽혀질 것이므로,'XmlReader'로 남겨진 유일한 것은 그것을 처리하는 것입니다. 그들이 "되감기"를 원한다면 어떤 식 으로든 (문자열, XmlDocument 등) 어떤 식 으로든 다시 저장할 수 있어야하고, XmlReader는'ReadSubtree() 'Dispose() '를 호출해도 "부모"독자의 추가 읽기가 금지되지 않습니다. 허락 해 줘서, 나는 스택에서 더 높은 '사용중'을 가질 가능성이 있지만, 어딘가에서 완전한 사용에 있어야하므로, 좋은 연습을 나타 내기 위해 그것을 넣었습니다. –

+1

메서드에 전달 된 리소스를 삭제하는 것이 모범 사례가 아닙니다. –

관련 문제