31

これは、私が解決しようとしている問題の架空の例です。C# で作業していて、次のような XML があるとします。

<?xml version="1.0" encoding="utf-8"?>
<Cars>
  <Car>
    <StockNumber>1020</StockNumber>
    <Make>Nissan</Make>
    <Model>Sentra</Model>
  </Car>
  <Car>
    <StockNumber>1010</StockNumber>
    <Make>Toyota</Make>
    <Model>Corolla</Model>
  </Car>
  <SalesPerson>
    <Company>Acme Sales</Company>
    <Position>
       <Salary>
          <Amount>1000</Amount>
          <Unit>Dollars</Unit>
    ... and on... and on....
  </SalesPerson>
</Cars>

SalesPerson 内の XML は非常に長く、メガバイトのサイズになることがあります。タグをデシリアライズしたいのですが、SalesPerson XML 要素をデシリアライズせずに、「後で」生の形式のままにします

基本的に、これを XML のオブジェクト表現として使用できるようにしたいと考えています。

[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlArrayItem(typeof(Car))]
    public Car[] Car { get; set; }

    public Stream SalesPerson { get; set; }
}

public class Car
{
    [System.Xml.Serialization.XmlElementAttribute("StockNumber")]
    public string StockNumber{ get; set; }

    [System.Xml.Serialization.XmlElementAttribute("Make")]
    public string Make{ get; set; }

    [System.Xml.Serialization.XmlElementAttribute("Model")]
    public string Model{ get; set; }
}

ここで、Cars オブジェクトの SalesPerson プロパティには、XmlSerializer を介して実行された後の <SalesPerson> xml 要素内にある生の xml を含むストリームが含まれます。

これはできますか?XML ドキュメントの「一部」だけをデシリアライズすることはできますか?

ありがとう!-マイク

How to Deserialize XML documentから盗まれた ps の例の xml

4

9 に答える 9

42

少し古いスレッドかもしれませんが、とにかく投稿します。私は同じ問題を抱えていました(1MBを超えるファイルから10kbのデータを逆シリアル化する必要がありました)。メイン オブジェクト (デシリアライザーである必要がある InnerObject を持つ) で、IXmlSerializable インターフェイスを実装し、ReadXml メソッドを変更しました。

input として xmlTextReader があり、最初の行は XML タグまで読み取ることです。

reader.ReadToDescendant("InnerObjectTag"); //tag which matches the InnerObject

次に、逆シリアル化するオブジェクトの型の XMLSerializer を作成し、逆シリアル化します。

XmlSerializer   serializer = new XmlSerializer(typeof(InnerObject));

this.innerObject = serializer.Deserialize(reader.ReadSubtree()); //this gives serializer the part of XML that is for  the innerObject data

reader.close(); //now skip the rest 

これにより、逆シリアル化にかかる時間を大幅に節約でき、XML の一部だけを読み取ることができます (ファイルを説明するいくつかの詳細であり、ユーザーがそのファイルを読み込もうとしているかどうかを判断するのに役立つ場合があります)。

于 2010-02-12T11:53:47.950 に答える
7

user271807 から受け入れられた回答は素晴らしい解決策ですが、次のような内部例外で例外を回避するために、フラグメントの xml ルートも設定する必要があることがわかりました。

...xmlns=''> was not expected

この例外は、この xml ドキュメントの内部の Authentication 要素のみを逆シリアル化しようとしたときに発生しました。

<?xml version=""1.0"" encoding=""UTF-8""?>
<Api>
  <Authentication>                       
      <sessionid>xxx</sessionid>
      <errormessage>xxx</errormessage>                
  </Authentication>
</ApI>

したがって、この拡張メソッドを再利用可能なソリューションとして作成することになりました-警告にはメモリリークが含まれています。以下を参照してください。

public static T DeserializeXml<T>(this string @this, string innerStartTag = null)
        {
            using (var stringReader = new StringReader(@this))
            using (var xmlReader = XmlReader.Create(stringReader)) {
                if (innerStartTag != null) {
                    xmlReader.ReadToDescendant(innerStartTag);
                    var xmlSerializer = new XmlSerializer(typeof(T), new XmlRootAttribute(innerStartTag));
                    return (T)xmlSerializer.Deserialize(xmlReader.ReadSubtree());
                }
                return (T)new XmlSerializer(typeof(T)).Deserialize(xmlReader);
            }
        }

2017 年 3 月 20 日更新: 以下のコメントが指摘しているように、XmlSerializer のコンストラクターの 1 つを使用するとメモリ リークの問題が発生するため、以下に示すようにキャッシュ ソリューションを使用することになりました。

    /// <summary>
    ///     Deserialize XML string, optionally only an inner fragment of the XML, as specified by the innerStartTag parameter.
    /// </summary>
    public static T DeserializeXml<T>(this string @this, string innerStartTag = null) {
        using (var stringReader = new StringReader(@this)) {
            using (var xmlReader = XmlReader.Create(stringReader)) {
                if (innerStartTag != null) {
                    xmlReader.ReadToDescendant(innerStartTag);
                    var xmlSerializer = CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute(innerStartTag));
                    return (T) xmlSerializer.Deserialize(xmlReader.ReadSubtree());
                }
                return (T) CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute("AutochartistAPI")).Deserialize(xmlReader);
            }
        }
    }
/// <summary>
///     A caching factory to avoid memory leaks in the XmlSerializer class.
/// See http://dotnetcodebox.blogspot.dk/2013/01/xmlserializer-class-may-result-in.html
/// </summary>
public static class CachingXmlSerializerFactory {
    private static readonly ConcurrentDictionary<string, XmlSerializer> Cache = new ConcurrentDictionary<string, XmlSerializer>();
    public static XmlSerializer Create(Type type, XmlRootAttribute root) {
        if (type == null) {
            throw new ArgumentNullException(nameof(type));
        }
        if (root == null) {
            throw new ArgumentNullException(nameof(root));
        }
        var key = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName);
        return Cache.GetOrAdd(key, _ => new XmlSerializer(type, root));
    }
    public static XmlSerializer Create<T>(XmlRootAttribute root) {
        return Create(typeof (T), root);
    }
    public static XmlSerializer Create<T>() {
        return Create(typeof (T));
    }
    public static XmlSerializer Create<T>(string defaultNamespace) {
        return Create(typeof (T), defaultNamespace);
    }
    public static XmlSerializer Create(Type type) {
        return new XmlSerializer(type);
    }
    public static XmlSerializer Create(Type type, string defaultNamespace) {
        return new XmlSerializer(type, defaultNamespace);
    }
}
于 2016-04-14T07:05:00.710 に答える
3

クラスに ISerializable インターフェイスを実装することで、シリアル化の実行方法を制御できます。これは、メソッド シグネチャ (SerializationInfo 情報、StreamingContext コンテキスト) を持つコンストラクターも意味することに注意してください。

ただし、ストリーミング メカニズムを使用する必要がない場合は、Linq to XML を使用して同じことを実現する方が簡単で、長期的に維持するのが簡単になるため、ストリーミングでこれを行う必要があるかどうかをよく見てください。期間 (IMO)

于 2008-12-15T21:58:12.883 に答える
2

ここで XML はバッキング ストアとして最適な選択ではないかもしれないという前のコメント者のコメントは正しいと思います。

規模の問題があり、変換などの XML で得られる他の優れた機能を利用していない場合は、データにデータベースを使用する方がよい場合があります。あなたが行っている操作は、実際にはそのモデルにより適しているようです。

これがあなたの質問に実際に答えているわけではないことはわかっていますが、使用できる代替ソリューションを強調したいと思いました. 優れたデータベースと、.netTiers、NHibernate、または最近では LINQ to SQL / Entity Framework などの適切な OR マッパーがあれば、コードベースの残りの部分への変更を最小限に抑えて、おそらく元に戻って実行できるでしょう。

于 2008-12-15T22:57:29.237 に答える
1

SalesPerson プロパティを type として定義してみてくださいXmlElement。これは、XML シリアル化を使用する ASMX Web サービスからの出力に対して機能します。入力でも機能すると思います。<SalesPerson>要素全体がXmlElement.

于 2009-07-27T18:42:36.930 に答える
1

通常、XML デシリアライゼーションはすぐに使用できる全か無かの命題であるため、おそらくカスタマイズする必要があります。完全な逆シリアル化を行わないと、SalesPerson 要素内で xml の形式が正しくないため、ドキュメントが無効になるリスクがあります。

そのリスクを受け入れる場合は、プレーン テキスト処理機能を使用して、基本的なテキスト解析を行い、SalesPerson 要素を別のドキュメントに分割してから、XML を処理することをお勧めします。

これは、XML が必ずしも正しい答えではない理由の良い例です。

于 2008-12-15T21:58:21.540 に答える
0

SalesPerson 要素を解析して文字列として保持するだけの場合は、「逆シリアル化」ではなく Xsl 変換を使用する必要があります。一方、SalesPerson 要素を解析して、他のすべての非 SalesPerson 要素からメモリ内のオブジェクトのみを設定する場合は、Xsl 変換も適している可能性があります。ファイルが非常に大きい場合は、それらを分離し、Xsl を使用して異なる xml ファイルを結合することを検討して、SalesPerson I/O が必要なときにのみ発生するようにすることができます。

于 2009-07-27T18:40:07.293 に答える