我一直在网上研究,包括Stack溢出,但要么我遗漏了一些东西,要么我看到的例子不适用于我的情况。
当我试图在XML序列化期间动态设置根元素和列表项元素名称时,我将收到此错误。
XmlRoot and XmlType attributes may not be specified for the type
System.Collections.Generic.List`1[
[XmlSerializationFailureExample.Controllers.MyClass, XmlSerializationFailureExample,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]我在微软网站上看到了一篇很老的文章,上面说这条消息应该读一读:Only XmlRoot and XmlType attributes may be specified...。当然,如果删除除XmlRoot和XmlType之外的所有重写,则会清除错误,但呈现的XML不符合我的需要。
我使用的是XmlSerializer重写构造函数,因为我必须动态地设置根和第一个子元素名称。相同的类需要在不同的情况下生成不同的XML元素名称。虽然这个示例只有两个字段,但实际序列化的类大约有100个字段。
因此,在直接序列化List<MyClass> 对象时,如何控制根元素和直接子元素的名称?
目标是使XML看起来像这样:
<ArrayOfPerson>
<Person>
<Name>John Doe</Name>
<Age>57</Age>
</Person>
<Person>
<Name>Doe, Jane</Name>
<Age/>
</Person>
</ArrayOfPerson>通过更改重写值,我应该能够从同一个类生成这样的XML:
<ArrayOfEmployee>
<Employee>
<Name>John Doe</Name>
<Age>57</Age>
</Employee>
<Employee>
<Name>Doe, Jane</Name>
<Age/>
</Employee>
</ArrayOfEmployee>下面是一些简单的代码,说明了我的问题。在这个例子中,我使用了Visual 2013模板中的一个基本MVC.Net应用程序。
// GET api/SerializationTest
public ActionResult SerializationTest()
{
var list = new List<MyClass> {
new MyClass {Name = "John Doe", Age = 57},
new MyClass {Name = "Doe, Jane"}
};
XmlAttributes xmlPerson = new XmlAttributes {
XmlRoot = new XmlRootAttribute { ElementName = "Person" }
};
XmlAttributes xmlPersonList = new XmlAttributes {
XmlRoot = new XmlRootAttribute { ElementName = "ArrayOfPerson" },
XmlArrayItems = {
new XmlArrayItemAttribute("Person",typeof(MyClass))
},
};
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(MyClass), xmlPerson);
overrides.Add(typeof(List<MyClass>), xmlPersonList);
return new XmlResult(
list,
"TestFile.xml",
overrides
);
}正在序列化的示例类。实际的类有大约100个属性。
public class MyClass
{
public string Name { get; set; }
public int? Age { get; set; }
}更新1
如果我将我的List<MyClass>封装在另一个类中,并按下面的方式用属性进行注释,我就可以得到我想要的XML。但是我如何动态地做到这一点,因为指定的元素名称在运行时必须有所不同?
[XmlRoot(ElementName = "ArrayOfPerson")]
public class MyCollection
{
[XmlElement(ElementName = "Person")]
public List<MyClass> Items { get; set; }
}更新1的端
XmlResult类型派生自内置ActionResult,并包含实际的序列化逻辑。类的目的是返回一个文件,而不是一个HTML页面。
public class XmlResult : ActionResult
{
private string FileName { get; set; }
private object ObjectToSerialize { get; set; }
private XmlAttributeOverrides Overrides { get; set; }
public XmlResult(object objectToSerialize, string fileName, XmlAttributeOverrides overrides)
{
ObjectToSerialize = objectToSerialize;
FileName = fileName;
Overrides = overrides;
}
public override void ExecuteResult(ControllerContext context)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-control", "no-store, no-cache");
HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename=" + FileName);
HttpContext.Current.Response.ContentType = "text/xml";
try
{
if (ObjectToSerialize != null)
{
var xs = new XmlSerializer(ObjectToSerialize.GetType(), Overrides);
xs.Serialize(HttpContext.Current.Response.Output, ObjectToSerialize);
}
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("<error>" + ex + "</error>");
}
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.SuppressContent = true;
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}发布于 2015-08-24 19:20:12
嗯,我无法使事情像我想要的那样动态,但这个解决方案似乎奏效了。
首先,我使我的类成为抽象的基类:
public abstract class MyBaseClass
{
public string Name { get; set; }
public int? Age { get; set; }
}然后,我为我想要的每个XML元素名称创建了派生类:
public class Person : MyBaseClass { }
public class Employee : MyBaseClass { }然后,确保在控制器操作方法中使用正确的派生类,这是一个简单的问题。
public ActionResult SerializePersonCollectionByXml()
{
var list = new List<Person> {
new Person {Name = "John Doe", Age = 57},
new Person {Name = "Doe, Jane"}
};
return new XmlResult(
list,
"PersonCollectionByXml.xml"
);
}
// GET api/SerializationTest
public ActionResult SerializeEmployeeCollectionByXml()
{
var list = new List<Employee> {
new Employee {Name = "John Doe", Age = 57},
new Employee {Name = "Doe, Jane"}
};
return new XmlResult(
list,
"EmployeeCollectionByXml.xml"
);
}当然,在实际应用中,事情要复杂一些.上面的技术允许我将所有属性和方法保留在基类中,只需使用派生类获得所需的XML元素名称。
真正的应用程序是根据动作方法的输入参数在运行时选择Person或Employee类。此外,逻辑被隐藏在另一组类中,其继承结构反映了这些类。由于.Net 4.6还不支持与泛型类型(只有接口和委托)的协变性,所以在通过XmlResult类返回它们之前,我必须再经历几次循环才能从内部逻辑获得正确的返回值。
示例:
内部服务类:
public abstract class ServiceBase<TRecord> : IDisposable where TRecord : MyBaseClass
{
public abstract List<TRecord> Search(SearchParams p);
}
public class ServicePerson : ServiceBase<Person>
{
public override List<Person> Search(SearchParams p)
{
var result = base.Search(p);
// for example only, just used a simple cast; more complex operation may be required.
return result.Select(r => (Person)r).ToList();
}
}
public class ServiceEmployee : ServiceBase<Employee>
{
public override List<Employee> Search(SearchParams p)
{
var result = base.Search(p);
// for example only, just used a simple cast; more complex operation may be required.
return result.Select(r => (Employee)r).ToList();
}
}控制员的行动方法:
public ActionResult Search(Guid apiKey, SearchParams p)
{
try
{
if (p.UsePerson)
{
using (var service = new ServicePerson())
{
List<Person> result = service.Search(p);
return XmlResult(result, "PersonList.xml");
}
}
using (var service = new ServiceEmployee())
{
List<Employee> result = service.Search(p);
return XmlResult(result, "EmployeeList.xml");
}
}
catch (Exception ex)
{
// log error
}
}https://stackoverflow.com/questions/32120950
复制相似问题