Возвращение динамического объекта вызывает ошибку во время выполнения - PullRequest
0 голосов
/ 08 октября 2018

Я занимаюсь разработкой веб-API, в котором метод GET должен возвращать объект, переменные которого будут определены на основе файла XML.Возвращаемый формат должен быть либо XML, либо JSON по запросу клиента.Я хочу вернуть данные из XML-файла в XML-формат клиенту и что-то разумное для JSON при запросе JSON.

Узлы в XML могут увеличиваться или уменьшаться и, следовательно, Iне может определить фиксированный класс в Моделях .Мое текущее решение - вернуть динамический объект, но я получаю исключение, показанное ниже.Что я могу сделать, чтобы избежать исключения?

GET 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>

1 Ответ

0 голосов
/ 08 октября 2018

Причина, по которой вы получаете эту ошибку, заключается в том, что вы объявили свой метод для возврата объекта типа object, но фактически вернули полиморфный подтип, а именно ExpandoObject.Поскольку DataContractSerializerXmlSerializer) откажется сериализовать неожиданные полиморфные типы, они выдают исключение, которое вы видите.Подробнее см.

При этом я хотел бы предложить другой, более простой подход.Во-первых, определите ваш метод Get(), чтобы явно возвращать XElement, например, так:

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

DataContractSerializerXmlSerializer) оба способны сериализовать объект этого типа (так как он реализует IXmlSerializable), поэтому ваш метод теперь будет успешно возвращать содержимое файла "D:\data.xml" дословно при запросе XML.

Теперь, что делать, когда запрашивается JSON?Как оказалось, в Json.NET есть встроенный конвертер XmlNodeConverter, который может сериализовать XElement в и из JSON.Он используется внутри JsonConvert.SerializeXNode(), но является общедоступным и может использоваться напрямую.Таким образом, если вы добавите конвертер в глобальный список конвертеров Web API в JsonSerializerSettings.Converters, ваш метод должен теперь возвращать что-то разумное и для JSON.

Вы не указываете, какая версиявеб-API, который вы используете.Чтобы добавить глобальный конвертер, см.

...