2012-02-09 4 views
1

현재 요소의 모든 값이 attibute로 변환되도록 C#에서 심층 구조화 된 XML 문서를 병합하려고합니다.XML 문서 병합

<members> 
    <member xmlns="mynamespace" id="1" status="1" notes="" url="someurl" altUrl="" date1="somedate" date2="someotherdate" description="some description" tags="" category="some category" contactId="1" contactPerson="some contact person" phone="" mobile="mobile number" email="[email protected]" /> 
</members> 

난 그냥 떨어져 요소 이름과 속성에 대한 구문 분석,하지만 이후 수 :

<members> 
    <member xmlns="mynamespace" id="1" status="1"> 
     <sensitiveData> 
      <notes/> 
      <url>someurl</url> 
      <altUrl/> 
      <date1>somedate</date1> 
      <date2>someotherdate</date2> 
      <description>some description</description> 
      <tags/> 
      <category>some category</category> 
     </sensitiveData> 
     <contacts> 
      <contact contactId="1"> 
       <contactPerson>some contact person</contactPerson> 
       <phone/> 
       <mobile>mobile number</mobile> 
       <email>[email protected]</email> 
      </contact> 
     </contacts> 
    </member> 
</members> 

은 내가 그것을 같이하고 싶은 것은 이것이다 : 다음과 같이

XML 구조입니다 이 XML은 내가 제어 할 수없는 webservice에서 온다. 구조로 이것을 평평하게하는 일종의 동적 파서를 만들어야한다. 어떤 시점에서 변경한다.

XML 구조가 웹 서비스의 XElement로 제공된다는 점에 유의해야합니다.

누구든지 이전에이 작업을 시도했는데 어떻게 공유 했습니까? :-) 그것은 매우 감사하겠습니다!

미리 감사드립니다.

모든 최선을,

+1

당신이 보여 XML이 유효로 추가 된 알 수 있듯이 업데이트 된 LINQ 표현이

var result = new XDocument(new XElement(doc.Root.Name, from x in doc.Root.Elements() select new XElement(x.Name, (from y in x.Descendants() where !y.HasElements select new XAttribute(y.Name.LocalName, y.Value)).Distinct(new XAttributeEqualityComparer()) ))); 

과 같을 것이다 ('/ kontakter'가있다 여는 태그가 없습니까?) ... 질문에 관해서는 일반적인 답은 없습니다. 왜냐하면 모든 규칙은 적용하려는 규칙 (예 : 둘 이상의 '연락처'등이있을 경우 어떻게 될 것인가)에 달려 있기 때문입니다. 내 질문은 : 왜 XML을 "평평하게"하고 싶습니까? – Yahia

+0

안녕 Yahia, 고마워 - 그냥 오타되었습니다 :) 나는 CMS로 가져올 수 있도록 이것을 평평하게해야합니다. – bomortensen

+0

다음 IMHO XSLT는 배포 후에도 변환 규칙을 변경할 수 있으므로 갈 방법입니다 ... – Yahia

답변

2

이 시도 :

var doc = XDocument.Parse(@"<members>...</members>"); 

var result = new XDocument(
    new XElement(doc.Root.Name, 
     from x in doc.Root.Elements() 
     select new XElement(x.Name, 
      from y in x.Descendants() 
      where !y.HasElements 
      select new XAttribute(y.Name.LocalName, y.Value)))); 

결과 :

<members> 
    <member notes="" url="someurl" altUrl="" date1="somedate" date2="someotherdate" description="some description" tags="" category="some category" contactPerson="some contact person" phone="" mobile="mobile number" email="[email protected]" xmlns="mynamespace" /> 
</members> 
0

보이 다른 XML 문서를 생성하고 있습니까, 아니면 그냥 당신이 간단하게 처리 할 수 ​​있도록하는 것입니다? 이전의 경우라면 리프 노드를 발견했을 때 모든 값을 맵에 넣으면됩니다. 실제로 맵의 키 - 값 쌍을 반복하여 속성만으로 xml 태그를 재구성 할 수 있습니다.

1

요소를 특성으로 변환하기 위해 XSLT 변환을 작성할 수 있습니다.

1

이 XSLT 1.0 스타일 시트를 사용할 수 있습니다. 여러 개의 <contact> 요소를 처리하는 방법을 수정할 수 있습니다.

입력 XML

<members> 
    <member xmlns="mynamespace" id="1" status="1"> 
    <sensitiveData> 
     <notes/> 
     <url>someurl</url> 
     <altUrl/> 
     <date1>somedate</date1> 
     <date2>someotherdate</date2> 
     <description>some description</description> 
     <tags/> 
     <category>some category</category> 
    </sensitiveData> 
    <contacts> 
     <contact contactId="1"> 
     <contactPerson>some contact person</contactPerson> 
     <phone/> 
     <mobile>mobile number</mobile> 
     <email>[email protected]</email> 
     </contact> 
     <contact contactId="2"> 
     <contactPerson>second contact person</contactPerson> 
     <phone/> 
     <mobile>second mobile number</mobile> 
     <email>second [email protected]</email> 
     </contact> 
    </contacts> 
    </member> 
</members> 

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:my="mynamespace" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="node()|@*"> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:template> 

    <xsl:template match="members|my:member"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="node()[text()][ancestor::my:member]|@*[ancestor::my:member]"> 
    <xsl:variable name="vContact"> 
     <xsl:if test="ancestor-or-self::my:contact"> 
     <xsl:value-of select="count(ancestor-or-self::my:contact/preceding-sibling::my:contact) + 1"/> 
     </xsl:if> 
    </xsl:variable> 
    <xsl:attribute name="{name()}{$vContact}"> 
     <xsl:value-of select="."/> 
    </xsl:attribute> 
    </xsl:template> 

</xsl:stylesheet> 

<members> 
    <member xmlns="mynamespace" id="1" status="1" url="someurl" date1="somedate" 
      date2="someotherdate" 
      description="some description" 
      category="some category" 
      contactId1="1" 
      contactPerson1="some contact person" 
      mobile1="mobile number" 
      email1="[email protected]" 
      contactId2="2" 
      contactPerson2="second contact person" 
      mobile2="second mobile number" 
      email2="second [email protected]"/> 
</members> 
2

내가 DTB 대답은 생각 XML 출력 가장 좋은 방법. 그러나 한 가지 중요한 문제에 주목해야합니다. 다른 연락처 정보를 추가하려고하면 dtb 코드가 다운됩니다. 회원은 둘 이상의 연락처 정보를 가질 수 있지만 중복 된 속성을 가질 수 없기 때문입니다. 이 문제를 해결하기 위해 고유 한 속성 만 선택하도록 코드를 업데이트했습니다. 그렇게하기 위해 IEqualityComparer<XAttribute>을 구현했습니다. 당신이 고유 전화 사용자 지정 같음 비교 과부하 (XAttributeEqualityComparer)

class XAttributeEqualityComparer : IEqualityComparer<XAttribute> 
    { 
     public bool Equals(XAttribute x, XAttribute y) 
     { 
      return x.Name == y.Name; 
     } 

     public int GetHashCode(XAttribute obj) 
     { 
      return obj.Name.GetHashCode(); 
     } 
    }