我遇到了一个问题,想知道是否有简单的解决方法。
这里我有一个XML模板,定义了一些属性及其值。
<Properties>
<Property name="ID">10000</Property>
<Property name="Name">
<SubProperty name="FirstName">Foo</SubProperty>
<SubProperty name="LastName">Bar</SubProperty >
</Property>
</Properties>
我只需提取模板中定义的Properties / subProperties来生成一个新的XML文件,并附上所有值,如
<Items>
<ID>10000</ID>
<Name>
<FirstName>Foo</FirstName>
<LastName>Bar</LastName>
</Name>
</Items>
由于我在设计时不知道模板的内容,我尝试加载它并使用LINQ创建了List类,但在直接序列化时无法得到上面的结果。因此,我不是创建一个List类,而是使用Reflection.Emit创建一个动态对象,然后将该对象序列化为XML。
private static readonly XDocument doc = XDocument.Load("Template.xml");
static void Main(string[] args) {
var newType = CreateDynamicType();
var newObject = Activator.CreateInstance(newType);
var properties = newType.GetProperties();
foreach (var property in properties) {
// assign values
}
SerializeToXml(newObject);
}
private static Type CreateDynamicType() {
AssemblyName assemblyName = new AssemblyName() { Name = "DynamicTypeAdapter" };
AssemblyBuilder assembly =
Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder module =
assembly.DefineDynamicModule(assembly.GetName().Name, false);
TypeBuilder type = module.DefineType("Items", TypeAttributes.Public | TypeAttributes.Class);
foreach (var p in doc.Descendants("Property")) {
string pName = p.Attribute("name").Value;
TypeBuilder subType = module.DefineType(pName, TypeAttributes.Public | TypeAttributes.Class);
foreach (var sp in p.Descendants("SubProperty")) {
CreateDynamicProperty(subType, sp.Attribute("name").Value, typeof(string));
}
var propertyType = subType.CreateType();
CreateDynamicProperty(type, pName, propertyType);
}
return type.CreateType();
}
private static void CreateDynamicProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType) {
PropertyBuilder property = typeBuilder.DefineProperty(propertyName,
PropertyAttributes.None, propertyType, new Type[] { typeof(string) });
FieldBuilder field = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
MethodAttributes GetSetAttributes = MethodAttributes.Public | MethodAttributes.HideBySig;
MethodBuilder getMethod =
typeBuilder.DefineMethod("get_value", GetSetAttributes, propertyType, Type.EmptyTypes);
ILGenerator getIL = getMethod.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, field);
getIL.Emit(OpCodes.Ret);
MethodBuilder setMethod =
typeBuilder.DefineMethod("set_value", GetSetAttributes, null, new Type[] { typeof(string) });
ILGenerator setIL = setMethod.GetILGenerator();
setIL.Emit(OpCodes.Ldarg_0);
setIL.Emit(OpCodes.Ldarg_1);
setIL.Emit(OpCodes.Stfld, field);
setIL.Emit(OpCodes.Ret);
property.SetGetMethod(getMethod);
property.SetSetMethod(setMethod);
}
它工作正常,但有没有简单的方法这样做? 任何评论表示赞赏。感谢
答案 0 :(得分:2)
如果您只想将一种XML格式更改(转换)为另一种XML格式,那么我认为您采用的方法并不是最合适的。框架中还有其他API支持这种功能。在您的情况下,要求似乎相当简单,所以我会选择Linq To Xml选项。以下是一个快速示例,它可以生成所需的输出。
XDocument doc = XDocument.Parse(@"<Properties>
<Property name='ID'>10000</Property>
<Property name='Name'>
<SubProperty name='FirstName'>Foo</SubProperty>
<SubProperty name='LastName'>Bar</SubProperty>
</Property>
</Properties>");
XElement items = new XElement("Items",
from property in doc.Descendants("Property")
select new XElement((string)property.Attribute("name"),
// If there are no child elements (SubPropety)
// get the property value
property.HasElements ? null : (string)property,
// Another way for checking if there are any child elements
// You could also use property.HasElements like the previous statement
property.Elements("SubProperty").Any() ?
from subproperty in property.Elements("SubProperty")
select new XElement((string)subproperty.Attribute("name"),
(string)subproperty) : null)
);
一些可能有帮助的资源包括:
http://msdn.microsoft.com/en-us/library/bb387098.aspx
http://msdn.microsoft.com/en-us/library/bb308960.aspx
http://iqueryable.com/2007/08/03/TransformingXMLWithLINQToXML.aspx
答案 1 :(得分:1)
根据你的上述内容,这样简单的事情应该有效。
var root = XElement.Parse(xml);
var result = new XElement("Items");
foreach (var p in root.Descendants("Property"))
{
var subs = p.Descendants("SubProperty").Select( sp => Transpose(sp) );
// The trick is here - XElement constructor uses params object[],
// so we can pass an arbitrary number of arguments to build the XElement
var item = new XElement( p.Attribute("name").Value, subs, subs.Any() : null ? p.Value );
result.Add( item );
}
// Transpose method
XElement Transpose(XElement xe)
{
return new XElement( xe.Attribute("name").Value, xe.Value );
}
// result
<Items>
<ID>10000</ID>
<Name>
<FirstName>Foo</FirstName>
<LastName>Bar</LastName>
</Name>
</Items>
注意:如果您有多个嵌套级别,或者需要能够区分模板中的多个Item
节点,则需要对其进行更多考虑。