2013-10-01 36 views
0

首先,我受.NET 2.0的限制,所以LINQ對我來說不是一種選擇(儘管如果它很簡單,我會很好奇看到一個LINQ解決方案作爲推動項目移植到.NET 3.5的飼料)。如何將反序列化的XML對象(.NET)轉換爲點分隔的命名鍵值的單個集合?

我有一個XSD在構建時通過xsd.exe變成了一組C#類。在運行時,一個XML文件被加載並反序列化到C#類中(此時驗證發生)。然後,我需要將該內存中的配置對象(包括在導入XML文件期間填充的默認值)轉換爲鍵值對的字典。

我想字典鍵是一個點分隔的路徑值。屬性值和元素文本將被視爲值,其他的一切都是關鍵。

作爲一個例子,假設下面的XML文件:

<rootNode> 
    <foo enabled="true"/> 
    <bar enabled="false" myAttribute="5.6"> 
     <baz>Some Text</baz> 
     <baz>Some other text.</baz> 
    </bar> 
</rootNode> 

會變成一個字典,像鑰匙:

注意
"rootNode.foo.enabled" = (Boolean)true 
"rootNode.bar.enabled" = (Boolean)false 
"rootNode.bar.myAttribute" = (Float)5.6 
"rootNode.bar.baz" = List<String> { "Some Text", "Some other text." } 

事情是根節點都不放過,不是因爲它是特殊的,但因爲它沒有文字或屬性。另外,字典是一個適當類型化的對象字典(這已經在反序列化中完成了,這是我希望使用C#對象而不是直接使用XML的原因之一)。

有趣的是,由xsd.exe創建的對象已經是真的接近我想要的形式。類名稱就像rootNodeFoo,其上有一個名爲myAttribute的浮動字段。

我已經考慮過的事情之一,但不知道如何去使用反射來迭代對象樹,並使用每個對象的類的名稱來找出節點的名稱(我可能有稍微調整一下外殼)。問題在於它感覺就像是錯誤的解決方案,因爲我已經可以訪問一個解串器了,它應該能夠爲我做所有這些工作,並且更快。

另一種選擇是使用XSLT將數據直接序列化爲我想要的格式。這裏的問題是,我的XSLT知識是有限的,我相信(糾正我,如果我錯了),我將失去打字的方式(一切都將是一個字符串),所以我將不得不基本反序列化再次手動獲取類型退出(這次沒有使用.NET解串器時得到的XSD驗證)。

如果它的事項,我使用獲得從XML文件填充配置對象的調用是這樣的:

var rootNode = new XmlRootAttribute(); 
rootNode.ElementName = "rootNode"; 
rootNode.Namespace = "urn:myNamespace"; 
var serializer = new XmlSerializer(typeof(rootNode), rootNode); 
using (var reader = new StringReader(xmlString)) 
{ 
    var deserializedObject = (rootNode)serializer.Deserialize(reader); 
} 
+0

我不認爲我完全理解你要做什麼。你說你想要一個帶有字符串鍵的字典,它可以包含不同的值(bool,float等)。唯一的方法是用'Dictionary '。但是你擔心失去「在路上打字」。也許你可以說明你如何建議使用字典。 – CSJ

+0

Dictionary 是我的計劃,然後在使用它們時將類型轉換爲適當的類型。回想一下,我可能使用「[strong] typing」錯誤,因爲編譯器不會爲我捕獲任何東西。關鍵是我不必經過從字符串轉換它們的努力。例如,序列化程序知道XML中布爾型類型字段的所有不同形式的「true」,我不知道(我也不相信自己得到它)。 –

回答

2

第一次觀察:使用對象圖不是開始生成點表示的最佳位置。你在說的節點其中有名稱,並在定義明確的層次結構並且你想要產生某種點符號; XML DOM似乎是做這件事的最好的地方。

您描述問題的方式存在一些問題。

第一個策略是處理多個同名的元素。你已經通過使該字典值實際列表迴避了這個問題,在你的榜樣,但假設你的XML是這樣的:

<rootNode> 
    <foo enabled="true"> 
     <bar enabled="false" myAttribute="5.6" /> 
     <bar enabled="true" myAttribute="3.4" /> 
    </foo> 
</rootNode> 

除了foo.enabled = (Boolean)true這應該是相當明顯的,什麼字典鍵你提出兩個myAttribute葉子?或者你會有一個單一的條目,foo.bar.myAttribute = List<float> {5.6, 3.4}?所以,問題1,沒有明確的方式來處理多個相似命名的非葉節點。

第二個問題是選擇一個數據類型在葉節點進行最終轉換(即屬性或元素值)。如果您正在寫入Dictionary<string, object>,您可能需要根據正在讀取的元素/屬性的Schema簡單類型來選擇一種類型。我不知道該怎麼做,但建議查找System.Convert類的各種用途。

假設爲這個問題#1不會浮出水面,那你真行與Dictionary<string, string>實現的那一刻,這裏的一些代碼,讓你開始:

static void Main(string[] args) 
{ 
    var xml = @" 
<rootNode> 
    <foo enabled=""true""> 
     <bar enabled=""false"" myAttribute=""5.6"" /> 
     <baz>Text!</baz> 
    </foo> 
</rootNode> 
"; 

    var document = new XmlDocument(); 
    document.LoadXml(xml); 
    var retVal = new Dictionary<string, string>(); 
    Go(retVal, document.DocumentElement, new List<string>()); 
} 

private static void Go(Dictionary<string, string> theDict, XmlElement start, List<string> keyTokens) 
{ 
    // Process simple content 
    var textNode = start.ChildNodes.OfType<XmlText>().SingleOrDefault(); 
    if (textNode != null) 
    { 
     theDict[string.Join(".", keyTokens.ToArray())] = textNode.Value; 
    } 

    // Process attributes 
    foreach (XmlAttribute att in start.Attributes) 
    { 
     theDict[string.Join(".", keyTokens.ToArray()) + "." + att.Name] = att.Value; 
    } 

    // Process child nodes 
    foreach (var childNode in start.ChildNodes.OfType<XmlElement>()) 
    { 
     Go(theDict, childNode, new List<string>(keyTokens) { childNode.Name }); // shorthand for .Add 
    } 
} 

而這裏的結果:

Result from running sample code

0

一個辦法是實施客戶格式,它插槽到標準串行化模式中,創建執行IFormatter即MyDotFormatter

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iformatter.aspx

然後實現一類如下

Stream stream = File.Open(filename, FileMode.Create); 
MyDotFormatter dotFormatter = new MyDotFormatter(); 
Console.WriteLine("Writing Object Information"); 
try 
{ 
dotFormatter.Serialize(stream, objectToSerialize); 
} 
catch (SerializationException ex) 
{ 
Console.WriteLine("Exception for Serialization data : " + ex.Message); 
throw; 
} 
finally 
{ 
stream.Close(); 
Console.WriteLine("successfully wrote object information"); 
} 
+0

使用格式化程序可能是我的解決方案中最合適的入口點,但是如果我沒有弄錯,我仍然必須通過反射手動導航對象圖形,並手工找出沿途所有內容的名稱。除非我錯過了什麼? –

相關問題