首先,我受限于.NET 2.0,所以LINQ对我来说不是一个选项(尽管我很想看到一个LINQ解决方案作为推动如果很容易转移到.NET 3.5的项目的素材)
我有一个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." }
值得注意的是,rootNode不是因为它是特殊的而是因为它没有文本或属性。此外,字典是一个适当键入的对象字典(这已经在反序列化中完成,这是我希望使用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 :(得分: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
}
}
这是结果:
答案 1 :(得分: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");
}