Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Matt Taylor 873 posts 2086 karma points
    May 24, 2022 @ 13:07
    Matt Taylor
    0

    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:

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

    Thanks,

    Matt

  • Dave Woestenborghs 3504 posts 12133 karma points MVP 8x admin c-trib
    May 24, 2022 @ 13:30
    Dave Woestenborghs
    100

    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

  • Matt Taylor 873 posts 2086 karma points
    May 24, 2022 @ 15:31
    Matt Taylor
    0

    Thanks, creating some duplicate classes for the generated models, mapping the properties and using those classes instead has worked.

    Cheers,

    Matt

  • Anders Bjerner 487 posts 2989 karma points MVP 7x admin c-trib
    May 24, 2022 @ 17:19
    Anders Bjerner
    0

    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:

    string rawJson = JsonConvert.SerializeObject(data, Formatting.None, new JsonSerializerSettings {
        ContractResolver = new YourContractResolver()
    });
    
    return new HttpResponseMessage(statusCode) {
        Content = new StringContent(rawJson, Encoding.UTF8, "application/json")
    };
    
  • Matt Taylor 873 posts 2086 karma points
    May 24, 2022 @ 21:41
    Matt Taylor
    0

    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

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

    Postman app JSON

    Anyhow, you have given me a good pointer. Thanks.

  • Anders Bjerner 487 posts 2989 karma points MVP 7x admin c-trib
    May 25, 2022 @ 09:10
    Anders Bjerner
    1

    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.

Please Sign in or register to post replies

Write your reply to:

Draft