2012-07-13 2 views
2

웹 서비스에서 사용하기 위해 XML로 직렬화되는 클래스가 있습니다. 이 클래스 인스턴스에서 XML은 웹 서비스가 읽을 수있는 CDATA 섹션을 포함해야하지만이를 구현하는 방법에 대한 손실이 있습니다.클래스를 XML로 직렬화하고 CDATA 섹션을 포함하는 문제가 발생했습니다.

<UpdateOrderStatus> 
    <Action>2</Action> 
     <Value> 
      <![CDATA[ 
       <Shipment> 
        <Header> 
         <SellerID> 
          ... 
      ]]> 
     </Value> 
</UpdateOrderStatus> 

내가 CDATA 부분을 제외하고, 해당 XML을 생성 할 수 있어요 :

XML은 같이 할 필요가있다.

public class UpdateOrderStatus 
{ 
    public int Action { get; set; } 


    public ValueInfo Value { get; set; } 

    public UpdateOrderStatus() 
    { 
     Value = new ValueInfo(); 
    } 


    public class ValueInfo 
    { 
     public ShipmentInfo Shipment { get; set; } 

     public ValueInfo() 
     { 
      Shipment = new ShipmentInfo(); 
     } 

     public class ShipmentInfo 
     { 
      public PackageListInfo PackageList { get; set; } 
      public HeaderInfo Header { get; set; } 
      public ShipmentInfo() 
      { 
       PackageList = new PackageListInfo(); 
       Header = new HeaderInfo(); 
      } 

     .... 

내가 사용에 대한 몇 가지 제안을 보았다 :

내 클래스 구조는 아래와 같다

[XmlElement("node", typeof(XmlCDataSection))] 

을하지만 나는 또한

[XmlElement("Value" + "<![CDATA[")] 
을 시도

예외가 발생합니다

결과 XML이 잘못 표시됨

<Value_x003C__x0021__x005B_CDATA_x005B_> 
.... 
</Value_x003C__x0021__x005B_CDATA_x005B_> 

누구든지 내가 뭘 잘못하고 있는지 보여 줄 수 있습니까? carlosfigueira 당 shipmentInfo가 직렬화하기

--Edit--

그러나 내가 추가 취득, 대부분의 경우 작동? 결과 XML 문자 (자세한 내용은 Writing an XML fragment using XmlWriterSettings and XmlSerializer is giving an extra character를 게시 참조) 이와 같이

내가에 쓰기 XML 방식을 변경 :

public void WriteXml(XmlWriter writer) 
     { 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
       ns.Add("", ""); 

       XmlWriterSettings settings = new XmlWriterSettings(); 

       settings.OmitXmlDeclaration = true; 
       settings.Encoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: false); 
       settings.Indent = true; 

       using (XmlWriter innerWriter = XmlWriter.Create(ms, settings)) 
       { 
        shipmentInfoSerializer.Serialize(innerWriter, this.Shipment,ns); 
        innerWriter.Flush(); 
        writer.WriteCData(Encoding.UTF8.GetString(ms.ToArray())); 
       } 
      } 
     } 

그러나 나는 예외 받고 있지 않다 :

System.InvalidOperationException: There was an error generating the XML document. ---> System.ArgumentException: '.', hexadecimal 
value 0x00, is an invalid character. 

을 - 편집 -

예외는 이전 serializeToString 메소드가 포함되어 발생했습니다. CDATA 출력을 제거하는 것이 띄어쓰기 문제를 제외하고는 올바른 것이지만 네임 스페이스와 xml 선언도 가져오고 있습니다.이 선언은 지정된 XML 설정에 따라 제거해야합니다. 출력 :

<?xml version="1.0"?> 
<UpdateOrderStatus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Action>1</Action> 
    <Value><![CDATA[< S h i p m e n t I n f o > 
    < P a c k a g e L i s t > 
     < P a c k a g e > 
      < S h i p D a t e > 2 0 1 2 - 0 7 - 1 3 T 1 1 : 5 8 : 5 1 . 0 9 2 5 6 1 5 - 0 4 : 0 0 </S h i p D a t e > 
      < I t e m L i s t > 
       < I t e m > 
        < S h i p p e d Q t y > 0 </S h i p p e d Q t y > 
       </I t e m > 
      </I t e m L i s t > 
     </P a c k a g e > 
    </P a c k a g e L i s t > 
    < H e a d e r > 
     < S e l l e r I d > S h i p m e n t h e a d e r </S e l l e r I d > 
     < S O N u m b e r > 0 </S O N u m b e r > 
    </H e a d e r > 
</S h i p m e n t I n f o > ]]></Value> 
</UpdateOrderStatus> 

새로운 클래스를 사용하여 BOM을 피하는 아이디어는 있습니까?

- 편집 3 - 성공!

나는 변화가 아래에 제시 이제 다음과 같은 작가 클래스와 테스트 방법이 구현 :

UpdateOrderStatus obj = new UpdateOrderStatus(); 

     obj.Action = 1; 
     obj.Value = new UpdateOrderStatus.ValueInfo(); 
     obj.Value.Shipment = new UpdateOrderStatus.ValueInfo.ShipmentInfo(); 
     obj.Value.Shipment.Header.SellerId = "Shipment header"; 
     obj.Value.Shipment.PackageList = new UpdateOrderStatus.ValueInfo.ShipmentInfo.PackageListInfo(); 
     obj.Value.Shipment.PackageList.Package = new UpdateOrderStatus.ValueInfo.ShipmentInfo.PackageListInfo.PackageInfo(); 
     obj.Value.Shipment.PackageList.Package.ShipDate = DateTime.Now; 



     XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 
     XmlWriterSettings settings = new XmlWriterSettings(); 
     settings.OmitXmlDeclaration = true; 
     settings.Encoding = new UTF8Encoding(false); 
     settings.Indent = true; 
     XmlSerializer xs = new XmlSerializer(typeof(UpdateOrderStatus)); 
     MemoryStream ms = new MemoryStream(); 


     XmlWriter writer = XmlWriter.Create(ms, settings); 
     xs.Serialize(writer, obj, ns); 
     Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); 
    } 


public void WriteXml(XmlWriter writer) 
     { 

      XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
      ns.Add("", ""); 

      XmlWriterSettings settings = new XmlWriterSettings(); 

      settings.OmitXmlDeclaration = true; 
      settings.Indent = true; 

      StringBuilder sb = new StringBuilder(); 
      using (XmlWriter innerWriter = XmlWriter.Create(sb, settings)) 
      { 
       shipmentInfoSerializer.Serialize(innerWriter, this.Shipment, ns); 
       innerWriter.Flush(); 
       writer.WriteCData(sb.ToString()); 
      } 
     } 

이 다음과 같은 XML이 생성

<UpdateOrderStatus> 
    <Action>1</Action> 
    <Value><![CDATA[<ShipmentInfo> 
    <PackageList> 
    <Package> 
     <ShipDate>2012-07-13T14:05:36.6170802-04:00</ShipDate> 
     <ItemList> 
     <Item> 
      <ShippedQty>0</ShippedQty> 
     </Item> 
     </ItemList> 
    </Package> 
    </PackageList> 
    <Header> 
    <SellerId>Shipment header</SellerId> 
    <SONumber>0</SONumber> 
    </Header> 
</ShipmentInfo>]]></Value> 
</UpdateOrderStatus> 
+0

CDATA 섹션은 XML을 쉽게 읽을 수 있도록 이스케이프 처리하는 방법입니다. CDATA의 내용은 XML이 아닙니다. 은 < 테스트와 일치합니다./> 내용이 항상 유효한 Xml 문서라고 확신하는 경우 문서를 사전 처리하여 CDATA를 제거하고 CDATA 섹션의 내용을 이스케이프 처리 할 수 ​​있어야합니다 . 내용이 유효한 Xml이 아니면 전체 문서가 유효하지 않게됩니다. 또 다른 옵션은 아래에 설명 된대로 IXmlSerializable을 구현하는 것입니다. 일단 시작하면 확장되고 유지 관리가 어려울 수 있습니다. – Pawel

답변

4

, 그것은이다.

시도 :

settings.Encoding = new Utf8Encoding(false); 

이 편집 : 또한

MemoryStream의 형식 하지 반드시 유효한 UTF-8 인코딩 된 문자열입니다주의! MemoryStream 대신 StringBuilder을 사용하여 내부 작성자를 만들 수 있습니다.

public void WriteXml(XmlWriter writer) 
    { 
     XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 

     XmlWriterSettings settings = new XmlWriterSettings(); 

     settings.OmitXmlDeclaration = true; 
     settings.Indent = true; 

     StringBuilder sb = new StringBuilder(); 
     using (XmlWriter innerWriter = XmlWriter.Create(sb, settings)) 
     { 
      shipmentInfoSerializer.Serialize(innerWriter, this.Shipment,ns); 
      innerWriter.Flush(); 
      writer.WriteCData(sb.ToString()); 
     } 
    } 
+0

링크 된 문서를 읽을 때 to, my line "XmlWriter writer = XmlWriter.Create (ms, settings);" 다른 곳에서 인코딩을 지정하지 않아도 인코딩에 나열된 인코딩을 유지해야하지만 예외가 발생하면 "System.InvalidOperationException : XML 문서를 생성하는 동안 오류가 발생했습니다."-> System.ArgumentException : . ', 16 진수 값 0x00은 잘못된 문자입니다. " –

+1

@RobertH 'MemoryStream' 콘텐츠가 반드시 유효한 UTF8 인코딩 된 문자열이 아니기 때문입니다. 내 대답을 업데이트했습니다. –

2

이 어떤 도움이 될 수를 : http://msdn.microsoft.com/en-us/library/system.xml.xmldocument.createcdatasection.aspx

//Create a CData section. 
XmlCDataSection CData; 
CData = doc.CreateCDataSection("All Jane Austen novels 25% off starting 3/23!");  

//Add the new node to the document. 
XmlElement root = doc.DocumentElement; 
root.AppendChild(CData); 

Console.WriteLine("Display the modified XML...");   
doc.Save(Console.Out); 

또한 예외 속성을 사용할 때 얻을 수 있습니까?

- 편집 -

당신은 사용자 정의 클래스를 추가하려고,이 같은 것을 할 수있는 : 얻을 것이다 IXmlSerializable 유형으로 여기 http://bytes.com/topic/net/answers/530724-cdata-xmltextattribute

+0

예외가 발생합니다 : System.InvalidOperationException : 임시 클래스 (결과 = 1)를 생성 할 수 없습니다. –

+0

웹 서비스 유효성 검사에 실패 할 값을 CDataElement로 바꾼다면이 [XmlElement ("CDataElement")] –

+0

과 같은 속성을 넣으십시오. –

2

구현 ShipmentInfo을 발견

some xml serializable class, 
{ 
    ....... 

    [XmlElement("PayLoad", Type=typeof(CDATA))] 
    public CDATA PayLoad 
    { 
     get { return _payLoad; } 
     set { _payLoad = value; } 
    } 
} 


public class CDATA : IXmlSerializable 
{ 
    private string text; 
    public CDATA() 
    {} 

    public CDATA(string text) 
    { 
     this.text = text; 
    } 

    public string Text 
    { 
     get { return text; } 
    } 

    /// <summary> 
    /// Interface implementation not used here. 
    /// </summary> 
    XmlSchema IXmlSerializable.GetSchema() 
    { 
     return null; 
    } 

    /// <summary> 
    /// Interface implementation, which reads the content of the CDATA tag 
    /// </summary> 
    void IXmlSerializable.ReadXml(XmlReader reader) 
    { 
     this.text = reader.ReadElementString(); 
    } 

    /// <summary> 
    /// Interface implementation, which writes the CDATA tag to the xml 
    /// </summary> 
    void IXmlSerializable.WriteXml(XmlWriter writer) 
    { 
     writer.WriteCData(this.text); 
    } 
} 

을 필요한 것에 가깝게 - 아래 예를 참조하십시오. 인코딩 당신이 (유니 코드 문자 당 2 바이트)를 사용하고 있기 때문에 '공간'당신의 편집 후보고 있습니다에 대한 응답으로

public class StackOverflow_11471676 
{ 
    public class UpdateOrderStatus 
    { 
     public int Action { get; set; } 
     public ValueInfo Value { get; set; } 
    } 
    [XmlType(TypeName = "Shipment")] 
    public class ShipmentInfo 
    { 
     public string Header { get; set; } 
     public string Body { get; set; } 
    } 
    public class ValueInfo : IXmlSerializable 
    { 
     public ShipmentInfo Shipment { get; set; } 
     private XmlSerializer shipmentInfoSerializer = new XmlSerializer(typeof(ShipmentInfo)); 

     public System.Xml.Schema.XmlSchema GetSchema() 
     { 
      return null; 
     } 

     public void ReadXml(XmlReader reader) 
     { 
      using (MemoryStream ms = new MemoryStream(
       Encoding.UTF8.GetBytes(
        reader.ReadContentAsString()))) 
      { 
       Shipment = (ShipmentInfo)this.shipmentInfoSerializer.Deserialize(ms); 
      } 
     } 

     public void WriteXml(XmlWriter writer) 
     { 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       using (XmlWriter innerWriter = XmlWriter.Create(ms, new XmlWriterSettings { OmitXmlDeclaration = true })) 
       { 
        shipmentInfoSerializer.Serialize(innerWriter, this.Shipment); 
        innerWriter.Flush(); 
        writer.WriteCData(Encoding.UTF8.GetString(ms.ToArray())); 
       } 
      } 
     } 
    } 
    public static void Test() 
    { 
     UpdateOrderStatus obj = new UpdateOrderStatus 
     { 
      Action = 1, 
      Value = new ValueInfo 
      { 
       Shipment = new ShipmentInfo 
       { 
        Header = "Shipment header", 
        Body = "Shipment body" 
       } 
      } 
     }; 

     XmlSerializer xs = new XmlSerializer(typeof(UpdateOrderStatus)); 
     MemoryStream ms = new MemoryStream(); 
     xs.Serialize(ms, obj); 
     Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); 
    } 
} 
+0

그래서 코드를 수정하면 일부 변경 사항이 적용됩니다. 여분을 제거해야합니까? 문자 (다른 게시물을 참조하십시오),하지만 그것의 예외를 던지고. 자세한 내용은 편집을 참조하십시오. –

+0

BOM을 제거하려면 XmlWriterSettings의'Encoding' 속성을 BOM이없는 인스턴스로 설정하십시오.'new XmlWriterSettings {OmitXmlDeclaration = true, Encoding = new UTF8Encoding (false)} ' – carlosfigueira

0

나는 carlosfigueira그냥 우아한 대답을했다 생각하지만, 아마도 하드 조금 첫눈에 이해. 고려해야 할 대안은 다음과 같습니다. UpdateOrderStatusShipmentInfo을 개별적으로 serialize/deserialize 할 수 있습니다. 당신이 Value 필드 XmlCDataSection를 사용해야합니다

[XmlRoot("UpdateOrderStatus")] 
    public class UpdateOrderStatus 
    { 
     [XmlElement("Action")] 
     public int Action { get; set; } 

     private String valueField; 
     [XmlElement("Value")] 
     public XmlCDataSection Value 
     { 
      get 
      { 
       XmlDocument xmlDoc = new XmlDocument(); 
       return xmlDoc.CreateCDataSection(valueField); 
      } 
      set 
      { 
       this.valueField = value.Value; 
      } 
     } 

     [XmlIgnore] 
     public ShipmentInfo Shipment 
     { 
      get; 
      set; 
     } 
    } 

    [XmlRoot("ShipmentInfo")] 
    public class ShipmentInfo 
    { 
     [XmlElement("Package")] 
     public String Package { get; set; } 
     [XmlElement("Header")] 
     public String Header { get; set; } 
    } 

참고 :

는 귀하의 비즈니스 객체 클래스를 정의합니다. 다음은 테스트/헬퍼 함수입니다 :

// Test function 
    const string t = @"<UpdateOrderStatus> 
     <Action>2</Action> 
      <Value> 
       <![CDATA[<ShipmentInfo> 
     <Package>packageInfo</Package> 
     <Header>headerInfo</Header> 
    </ShipmentInfo>]]> 
      </Value> 
    </UpdateOrderStatus>"; 

    static void Test1() 
    { 
     UpdateOrderStatus os = Deserialize(t); 

     String t2 = XmlUtil.Serialize(os); 
    } 

    // Helper functions 
    static UpdateOrderStatus Deserialize(String str) 
    { 
     UpdateOrderStatus os = XmlUtil.DeserializeString<UpdateOrderStatus>(str); 
     os.Shipment = XmlUtil.DeserializeString<ShipmentInfo>(os.Value.InnerText); 
     return os; 
    } 

    public static class XmlUtil 
    { 
     public static String Serialize<T>(T o) 
     { 
      XmlSerializer s = new XmlSerializer(typeof(T)); //, overrides); 
      //StringBuilder builder = new StringBuilder(); 

      MemoryStream ms = new MemoryStream(); 
      XmlWriterSettings settings = new XmlWriterSettings(); 
      settings.Encoding = Encoding.UTF8; 
      settings.Indent = true; 
      using (XmlWriter xmlWriter = XmlWriter.Create(ms, settings)) 
      { 
       XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
       ns.Add(String.Empty, String.Empty); 
       s.Serialize(xmlWriter, o, ns); 
      } 
      Byte[] bytes = ms.ToArray(); 
      // discard the BOM part 
      Int32 idx = settings.Encoding.GetPreamble().Length; 
      return Encoding.UTF8.GetString(bytes, idx, bytes.Length - idx); 
     } 

     public static T DeserializeString<T>(String content) 
     { 
      using (TextReader reader = new StringReader(content)) 
      { 
       XmlSerializer s = new XmlSerializer(typeof(T)); 
       return (T)s.Deserialize(reader); 
      } 
     } 

     ... 
    } 
1

예를 아래에있는 스키마의 구조가 정의 된 경우에만 그리고 당신은 스키마를 변경하는 선택의 여지가 없다.

[xmltext] 값을 deserialize/serialize 할 때 CDATA [] 의 텍스트를 묶는 것이 매우 어렵습니다. 당신은 xml에서 CDATA 값을 얻으려면 compiletransform을 사용할 수 있지만 C#에서 비 직렬화하고 메모리에로드하는 즉시 CDATA가 손실됩니다.

1을 수행하는 가장 간단한 방법 중 하나이다)가 최종 XML 출력이 도출되면/직렬화 2) 역 직렬화. 마지막 xml은 을 문자열로 변환하고 아래에 표시된 것처럼 구문 분석하여 string으로 반환하여 CDATA를 test1 값에 포함합니다.

문자열 xml = "@ # @! #! #! @ #! @ # % $ % @ # $ % # $ %";

XNamespace ns = @ "";

XDocument doc = XDocument.Parse (xml);

문자열 xmlString = string.Empty;

var coll = from doc.Descendants (ns + "test1") select query;

foreach는 (콜에서 VAR 값) {

value.ReplaceNodes (새 XCData (값 .Value));}

doc.save ("test.xml의"); // 문서를 변환합니다. tostring()

관련 문제