2

Web サービスで使用するために XML にシリアル化されたクラスがあります。このクラスのインスタンスでは、Web サービスが XML を読み取るための CDATA セクションを XML に含める必要がありますが、これを実装する方法がわかりません。

XML は次のようにする必要があります。

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

CDATA 部分を除いて、適切な 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 ごとに shippingInfo をシリアライズ可能にすることは、ほとんどの場合機能しますが、余分なものが得られますか? 結果の XML の文字 (詳細については、投稿XmlWriterSettings を使用して XML フラグメントを記述し、XmlSerializer が余分な文字を与えているを参照してください)

そのため、Write 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>
4

5 に答える 5

4

編集後に表示される「スペース」は、使用しているエンコーディング (Unicode、1 文字あたり 2 バイト) が原因です。

試す:

settings.Encoding = new Utf8Encoding(false);

編集:

MemoryStreamまた、 の形式は必ずしも有効な UTF-8 でエンコードされた文字列ではないことに注意してください。StringBuilder代わりにa を使用MemoryStreamして、内部ライターを作成できます。

    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());   
        }   
    }
于 2012-07-13T16:37:10.210 に答える
2

ShipmentInfo をIXmlSerializableタイプとして実装すると、必要なものに近づくことができます - 以下の例を参照してください。

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()));
    }
}
于 2012-07-13T15:07:46.317 に答える
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);

また、属性を使用したときにどのような例外が発生しましたか?

- 編集 -

カスタム クラスを追加してみて、次のようにすることもできます。

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

ここにあるようにhttp://bytes.com/topic/net/answers/530724-cdata-xmltextattribute

于 2012-07-13T14:10:46.143 に答える
0

カルロスフィゲイラはエレガントな答えを出しただけだと思いますが、一見すると少し理解しにくいかもしれません。検討のための代替案を次に示します。シリアライズ/デシリアライズUpdateOrderStatusShipmentInfo個別に行うことができます。

ビジネス オブジェクト クラスを次のように定義します。

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

フィールドに使用XmlCDataSectionする必要があることに注意してください。Valueテスト/ヘルパー関数は次のとおりです。

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

        ...
    }
于 2012-07-13T18:28:05.880 に答える