I have an old Umbraco 7 project that I am adding some WebAPI functions to.
Currently I'm calling the functions OK and returning simple strings etc. but if I try to return an IEnumerable list of my models then I'm getting:
<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 'Umbraco.Extensions.Models.Generated.Person' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
</ExceptionMessage>
<ExceptionType>
System.Runtime.Serialization.InvalidDataContractException
</ExceptionType>
<StackTrace>
at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type) at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type) at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type) at System.Runtime.Serialization.XmlObjectSerializerContext.GetDataContract(Int32 id, RuntimeTypeHandle typeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at WriteArrayOfFootballPackageToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract ) at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) 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.<>c__DisplayClass7.<WriteToStreamAsync>b__6() at System.Threading.Tasks.TaskHelpers.RunSynchronously(Action action, CancellationToken token)
</StackTrace>
</InnerException>
</Error>
Is there something simple I can do to get it to work?
I appreciate that this may not be an Umbraco issue but I have tried a couple of things that I have seen suggested regarding the Global.asax and WebApiConfig.cs and those did not have any effect so I'm scratching my head.
If you access your API method directly through the browser, you'll get an XML based response by default. Generally the models can't be converted to XML as Dave writes.
But if you're looking to output JSON, models can be serialized, but you need to be careful about circular references.
When using Newtonsoft.Json for serialization, you can specify your own custom contract revolver - eg. like ours here:
The 3rd party that have asked for this API have not specified whether they expect XML or JSON. I think they are happy to work with whatever I can give them.
You raise an interesting point that was confusing me though.
Like you say from my browser I get XML
<ArrayOfFootballPackageSerializable>
<FootballPackageSerializable>
<EventDateAndTime>2022-10-21T15:00:00</EventDateAndTime>
<EventTitle>Manchester City v Newcastle United</EventTitle>
<PackageType i:nil="true"/>
</FootballPackageSerializable>
<FootballPackageSerializable>
<EventDateAndTime>2022-11-18T15:00:00</EventDateAndTime>
<EventTitle>Arsenal v Leeds United</EventTitle>
<PackageType i:nil="true"/>
</FootballPackageSerializable>
<FootballPackageSerializable>
<EventDateAndTime>2022-11-18T15:00:00</EventDateAndTime>
<EventTitle>Arsenal v Leeds United</EventTitle>
<PackageType i:nil="true"/>
</FootballPackageSerializable>
</ArrayOfFootballPackageSerializable>
But I've been trying the Postman app and that seems to show JSON?
The default output format of WebAPI controllers are controlled by the Accept header sent to the API.
The browser doesn't send that it accepts JSON, so you get XML as output. Postman may send that it does in fact accept JSON, which is why you see a different result.
There are also a few ways that you can force your endpoint to always output JSON (or XML for that matter).
The example that I posted yesterday also forces the output to always be JSON.
How to make my models serializable for WebAPI?
I have an old Umbraco 7 project that I am adding some WebAPI functions to.
Currently I'm calling the functions OK and returning simple strings etc. but if I try to return an IEnumerable list of my models then I'm getting:
Is there something simple I can do to get it to work? I appreciate that this may not be an Umbraco issue but I have tried a couple of things that I have seen suggested regarding the Global.asax and WebApiConfig.cs and those did not have any effect so I'm scratching my head.
Thanks,
Matt
Hi Matt,
I assuming you want to return the models created by ModelsBuilder or models implementing IPublishedContent.
Those can't be serialized. You will need to create your own models and write some mapping.
Dave
Thanks, creating some duplicate classes for the generated models, mapping the properties and using those classes instead has worked.
Cheers,
Matt
Hi Matt,
Is your expected output format XML or JSON?
If you access your API method directly through the browser, you'll get an XML based response by default. Generally the models can't be converted to XML as Dave writes.
But if you're looking to output JSON, models can be serialized, but you need to be careful about circular references.
When using Newtonsoft.Json for serialization, you can specify your own custom contract revolver - eg. like ours here:
https://github.com/skybrud/Skybrud.Umbraco.Spa/blob/v3/latest/src/Skybrud.Umbraco.Spa/Json/Resolvers/SpaPublishedContentContractResolver.cs#L38-L65
The highlighted lines will ensure certain properties from
IPublishedContent
are ignored.Your API method may then be updated to something like this, where
data
is the model you wish to return:Hi Anders,
The 3rd party that have asked for this API have not specified whether they expect XML or JSON. I think they are happy to work with whatever I can give them.
You raise an interesting point that was confusing me though. Like you say from my browser I get XML
But I've been trying the Postman app and that seems to show JSON?
Anyhow, you have given me a good pointer. Thanks.
Hi again,
The default output format of WebAPI controllers are controlled by the
Accept
header sent to the API.The browser doesn't send that it accepts JSON, so you get XML as output. Postman may send that it does in fact accept JSON, which is why you see a different result.
There are also a few ways that you can force your endpoint to always output JSON (or XML for that matter).
The example that I posted yesterday also forces the output to always be JSON.
is working on a reply...