2017-10-09 54 views
2

這將將一個xml示例反序列化到「XmlModel」類中。將xml反序列化爲具有不同層次結構的類?

using System.Collections.Generic; 
using System.IO; 
using System.Xml.Serialization; 

namespace XmlTest 
{ 
    public class DeserializeXml 
    { 
     public XmlModel GetXmlModel() 
     { 
      string xml = @"<?xml version=""1.0"" encoding=""utf-16""?> 
      <root> 
       <foo> 
        <bar>1</bar> 
        <bar>2</bar> 
       </foo> 
      </root>"; 

      var dS = new XmlSerializer(typeof(XmlModel)); 

      var m = new XmlModel(); 
      using (var reader = new StringReader(xml)) 
      { 
       return (XmlModel) dS.Deserialize(reader); 
      } 
     } 
    } 

    [XmlRoot("root")] 
    public class XmlModel 
    { 
     [XmlArray("foo")] 
     [XmlArrayItem("bar")] 
     public List<string> Foo { get; set; } 
    } 
} 

這將讓模型:

var d = new DeserializeXml(); 
result = d.GetXmlModel(); 

我與舊代碼工作,我不能更改不是改變XMLATTRIBUTES其他XmlModel類。這裏的問題是:實際的XML沒有「foo」的節點:

string xml = @"<?xml version=""1.0"" encoding=""utf-16""?> 
<root> 
    <bar>1</bar> 
    <bar>2</bar> 
</root>"; 

所以,現在我堅持使解串器吞下這個XML輸出類型XmlModel的任務。這可能沒有Xslt預處理或其他更復雜的方法?

+0

您可以反序列化爲與XML匹配的格式,然後使用類似[AutoMapper](http://automapper.org/)的「升級」它。不是超級高性能,但我不知道你在這方面的要求,這可能是可以接受的。 –

+1

你還有什麼其他限制?你可以使用不同的反序列化方法嗎? –

+0

@Bradley Uffner使用代理類+ automapper是一個選項 - 但會涉及一些努力+性能損失,我認爲(比XSLT prrprocessor更重要)。一個替代的序列化程序是一個我沒有想到的選項,可以是一個解決方案,如果僅僅是XmlAttribute技巧將無法工作(我希望他們會)。 – TvdH

回答

1

可以使用XmlAttributeOverrides做指定使用這些屬性替換XML屬性爲您XmlModel,然後construct an XmlSerializer

var serializer = new XmlSerializer(typeof(XmlModel), overrides). 

但是,請注意從documentation以下警告:

爲了增加性能,XML序列化基礎結構動態生成程序集以序列化和反序列化指定的類型。基礎設施找到並重新使用這些程序集。使用下面的構造函數時,纔會出現這種情況:

XmlSerializer.XmlSerializer(類型)

XmlSerializer.XmlSerializer(類型,字符串)

如果你使用任何其他構造,同樣的多個版本程序集生成並且從不卸載,這會導致內存泄漏和性能下降。最簡單的解決方案是使用前面提到的兩個構造函數之一。否則,您必須緩存組件在一個Hashtable ...

下面的靜態類創建和緩存2個串行器,一個用於XmlModel「當前」版本,一個是「備用」版本,在其中<bar>元素缺乏外部容器元素:

public static class XmlModelSerializer<TRoot> 
{ 
    static XmlSerializer alternateSerializerInstance; 
    static XmlSerializer currentSerializerInstance; 

    public static XmlSerializer AlternateSerializerInstance { get { return alternateSerializerInstance; } } 

    public static XmlSerializer CurrentSerializerInstance { get { return currentSerializerInstance; } } 

    static XmlModelSerializer() 
    { 
     XmlAttributes alternateAttributes = new XmlAttributes 
     { 
      XmlElements = { new XmlElementAttribute("bar") }, 
     }; 
     XmlAttributeOverrides alternateOverrides = new XmlAttributeOverrides(); 
     alternateOverrides.Add(typeof(XmlModel), "Foo", alternateAttributes); 
     alternateSerializerInstance = new XmlSerializer(typeof(TRoot), alternateOverrides); 

     XmlAttributes currentAttributes = new XmlAttributes 
     { 
      XmlArray = new XmlArrayAttribute("foo"), 
      XmlArrayItems = { new XmlArrayItemAttribute("bar") }, 
     }; 
     XmlAttributeOverrides currentOverrides = new XmlAttributeOverrides(); 
     currentOverrides.Add(typeof(XmlModel), "Foo", currentAttributes); 
     currentSerializerInstance = new XmlSerializer(typeof(TRoot), currentOverrides); 
    } 
} 

通過使用兩個不同的串行器,每一個可能的XML格式,可以避免在所有變更任何遺留XmlModel類型。

然後,反序列化形式

<root> 
    <bar>1</bar> 
    <bar>2</bar> 
</root> 

的扁平XML你會簡單地做:在這兩種格式

var dS = XmlModelSerializer<XmlModel>.AlternateSerializerInstance; 
using (var reader = new StringReader(xml)) 
{ 
    return (XmlModel) dS.Deserialize(reader); 
} 

樣品fiddle顯示反序列化。

+0

這不僅證明在公共列表上使用[XmlElement(「bar」)] Foo {get;組; }將省略「foo」節點,它還顯示瞭如何在兩個反序列化之間動態切換 - grand。 – TvdH

1

如果你打開一個反序列化的替代方法,這將工作。它應該與XmlSerializer一樣快,如果不是更快。它只需在原始xml上打開一個XmlReader,移動到第一個「data」元素,將數據轉儲到列表中,然後從中填充並返回您的XmlModel

LINQPad文件可用here

public XmlModel GetXmlModel() 
{ 
    string xml = @"<?xml version=""1.0"" encoding=""utf-16""?> 
     <root> 
       <bar>1</bar> 
       <bar>2</bar> 
     </root>"; 
    using (var reader = XmlReader.Create(new StringReader(xml))) 
    { 
     reader.MoveToContent(); 
     var data = new List<string>(); 
     while (reader.Read()) 
     { 
      if (reader.NodeType == XmlNodeType.Element) 
      { 
       var element = XNode.ReadFrom(reader) as XElement; 
       switch (element.Name.LocalName) 
       { 
        case "bar": 
         { 
          data.Add(element.Value); 
          break; 
         } 
       } 
      } 
     } 
     return new XmlModel() { Foo = data }; 
    } 
} 

這顯然得到,如果你的bar類的,更復雜的超過簡單的內在的類型,比如string

+1

您的答案看起來好像是從http://msdn.microsoft.com/en-us/library/system.xml.linq.xnode.readfrom.aspx改編的,但不幸的是MSDN代碼有一個錯誤 - 它會跳過XML不縮進時的元素。請參閱[本答案](https://stackoverflow.com/a/18282052/3744182)進行分析。我個人建議只是加載到一個'XElement'中,並在內存中做所有事情,這正是因爲直接使用'XmlReader'非常挑剔。 – dbc

+0

我承認,我看過這個例子供參考。你的建議也是我的首選方式,但我試圖儘可能快地做到這一點,因爲在討論中提到了表現。 –

相關問題