返回动态对象会引发运行时错误

Returning a dynamic object is throwing runtime error

我正在开发一个 Web API,其中 GET 方法需要 return 一个对象,其变量将根据 XML 文件决定。根据客户的要求,returned 格式必须是 XML 或 JSON。我想 return 将 XML 文件中的数据转换为 XML 格式给客户端,并在请求 JSON 时对 JSON 进行一些合理的处理。

XML中的节点可能会增加或减少,因此我无法在模型[=中定义固定的class 28=]。我当前的解决方案是 return 一个动态对象,但我收到如下所示的异常。我该怎么做才能避免异常?

获取Api

[AllowAnonymous]
public class DataController : ApiController
{      
    //GET api/----based on dynamic binding
    public object Get()
    {
        //Read XML
        XDocument xDoc = XDocument.Load(@"D:\data.xml");

        string jsonStr = JsonConvert.SerializeXNode(xDoc);
        dynamic dynamicObject = JsonConvert.DeserializeObject<ExpandoObject>(jsonStr);


        return dynamicObject; //THIS LINE IS THROWING RUNTIME ERROR
    }
}

样本XML文件:

<Data>    
    <Name>abcd</Name>
    <bad>100</bad>
    <status>running</status>    
</Data> 

当我尝试访问 GET api 时,网页上出现以下错误:

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'System.Dynamic.ExpandoObject' with data contract name 'ArrayOfKeyValueOfstringanyType:http://schemas.microsoft.com/2003/10/Serialization/Arrays' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.
</ExceptionMessage>
<ExceptionType>
System.Runtime.Serialization.SerializationException
</ExceptionType>
<StackTrace>
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()
</StackTrace>
</InnerException>
</Error>

您收到该错误的原因是您已将方法声明为 return 类型为 object 的对象——但实际上 returned 了一个多态子类型,即ExpandoObject。由于 DataContractSerializer(和 XmlSerializer)将拒绝序列化意外的多态类型,因此它们会抛出您所看到的异常。详情见

话虽如此,我想建议一种不同的、更简单的方法。首先,将 Get() 方法明确定义为 return 和 XElement,如下所示:

public XElement Get()
{
    //Read XML
    XDocument xDoc = XDocument.Load(@"D:\data.xml");
    return xDoc.Root;
}

DataContractSerializer(和XmlSerializer)都能够序列化这种类型的对象(因为它implements IXmlSerializable),所以你的方法现在将成功return内容请求 XML 时逐字记录文件 "D:\data.xml"

现在,当请求 JSON 时该怎么办?事实证明,Json.NET 有一个内置转换器 XmlNodeConverter that can serialize an XElement to and from JSON. It's used internally by JsonConvert.SerializeXNode() but is public and so can be used directly. Thus if you add the converter to your global Web API list of converters in JsonSerializerSettings.Converters,你的方法现在应该 return 对 JSON 也是合理的。

您没有指定您使用的是哪个版本的 Web API。要全局添加转换器,请参阅

  • ASP.NET 网页 API 2:参见 How to set custom JsonSerializerSettings for Json.NET in MVC 4 Web API? and also the second part of this answer to Registering a custom JsonConverter globally in Json.Net。在这种情况下,您的代码将类似于:

    protected void Application_Start()
    {
        var config = GlobalConfiguration.Configuration;
        var settings = config.Formatters.JsonFormatter.SerializerSettings;
        settings.Converters.Add(new XmlNodeConverter());
    }
    
  • ASP.NET 核心 MVC:参见 JsonSerializerSettings and Asp.Net Core or 。在这里,您将再次访问全局 JsonSerializerSettings 并添加一个 XmlNodeConverter,如上所示。