JSON Serialization of collection

Posts   
 
    
superska avatar
superska
User
Posts: 35
Joined: 11-Nov-2013
# Posted on: 19-Nov-2013 16:34:37   

Hi,

I managed to serialize EntityObjects to JSON (for a WebAPI project) using this approach: http://www.llblgen.com/documentation/4.1/LLBLGen%20Pro%20RTF/Using%20the%20generated%20code/Adapter/Distributed%20systems/gencode_webservices.htm#decorating

However, I can't seem to be able to serialze a collection. I tried using a List<ProductEntity>, IEnumerable<ProductEntity>, ArrayList, but I keep getting this error:


<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 'XXX.XXX.Data.EntityClasses.ProductEntity' cannot be IXmlSerializable and have DataContractAttribute attribute.</ExceptionMessage><ExceptionType>System.Runtime.Serialization.InvalidDataContractException</ExceptionType><StackTrace>   at System.Runtime.Serialization.XmlDataContract.XmlDataContractCriticalHelper..ctor(Type type)
   at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.GetDataContract(Int32 id, RuntimeTypeHandle typeHandle, SerializationMode mode)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
   at WriteArrayOfanyTypeToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract )
   at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
   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>

Any ideas?

Thanks! Stefan Kamphuis

Walaa avatar
Walaa
Support Team
Posts: 14954
Joined: 21-Aug-2005
# Posted on: 20-Nov-2013 05:15:55   

Which runtime library version (build no.) are you using?

superska avatar
superska
User
Posts: 35
Joined: 11-Nov-2013
# Posted on: 20-Nov-2013 07:07:18   

Walaa wrote:

Which runtime library version (build no.) are you using?

Sorry, I'm using 4.1.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 20-Nov-2013 07:25:03   

Please post the code you are using that reproduces this error, to understand better your scenario. This should include: your service method, the decoration on the entity and some field, the WebApiConfig and the query-url that you are using to test that.

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39625
Joined: 17-Aug-2003
# Posted on: 20-Nov-2013 11:22:37   
Frans Bouma | Lead developer LLBLGen Pro
superska avatar
superska
User
Posts: 35
Joined: 11-Nov-2013
# Posted on: 20-Nov-2013 21:23:59   

Guys,

My colleague seems to have solved it by explicitly calling the JsonConvert method on the collection:


        public HttpResponseMessage GetEmptyProducts()
        {
            return Request.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(new ArrayList(){
                new ProductEntity(){ProductNo = 123456}
            }));
        }

Thanks so far anyway.

Stefan

superska avatar
superska
User
Posts: 35
Joined: 11-Nov-2013
# Posted on: 21-Nov-2013 13:17:33   

Hope it's okay to just reopen this thread.

The JSON we're getting out, now looks like this:

[{"_promotionProducts":null,"_fieldsData":[[null,null],[null,null],[null,null],[null,null], [null,null],[null,null],[null,null],[null,null],[null,null],[null,null],[null,null],[null,null], [123456,null],[null,null],[null,null],[null,null],[null,null],[null,null],[null,null]],"_fieldsFlags":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false, false,false,false,false,false,false,false,false,false,true,false,false,false,false,false, false,false,false,false,false,false,false,false],"_fieldsState":0,"_fieldsIsDirty": true,"_name":"ProductEntity","_isNew":true,"_validator":null,"_objectID": "02e45596-db12-4f65-9a99-4dfe3d86fef8","_concurrencyPredicateFactoryToUse":null, "_dataErrorInfoError":"","_dataErrorInfoErrorsPerField":null ,"_typeDefaultValueProvider":null,"_authorizerToUse":null ,"_auditorToUse":null,"_relatedEntitySyncInfos":null,"_field2RelatedEntity":null}]

Obviously, this is not what we need (for example: field names are not in there). I expected the JSon to be more like this:

{"ProductNo":"123456","Description":"Some Product"}

I guess that is still related to the serialization process, right?

We're using the DNN Service Framework (which is built on top of WebAPI), so I don't have a config.cs but the place where this is handled, contains this code after the MapHttpRoute call:

            var config = new HttpConfiguration();
            var json = config.Formatters.JsonFormatter;
            json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
            json.SerializerSettings.ContractResolver = new DefaultContractResolver()
            {
                IgnoreSerializableInterface = true,
                IgnoreSerializableAttribute = true
            }; 

The ProductEntity class looks like this (including some of the decorated members):


    // __LLBLGENPRO_USER_CODE_REGION_START AdditionalNamespaces
    // __LLBLGENPRO_USER_CODE_REGION_END
    /// <summary>Entity class which represents the entity 'Product'.<br/><br/></summary>
    [Serializable]
    [DataContract]
    public partial class ProductEntity : CommonEntityBase
        // __LLBLGENPRO_USER_CODE_REGION_START AdditionalInterfaces
        // __LLBLGENPRO_USER_CODE_REGION_END    
    {

        [DataMember]
        public virtual Nullable<System.Int32> CategoryId
        {
            get { return (Nullable<System.Int32>)GetValue((int)ProductFieldIndex.CategoryId, false); }
            set { SetValue((int)ProductFieldIndex.CategoryId, value); }
        }

        /// <summary> The CertificateDocument property of the Entity Product<br/><br/></summary>
        /// <remarks>Mapped on  table field: "Product"."CertificateDocument"<br/>
        /// Table field type characteristics (type, precision, scale, length): NVarChar, 0, 0, 2147483647<br/>
        /// Table field behavior characteristics (is nullable, is PK, is identity): true, false, false</remarks>
        [DataMember]
        public virtual System.String CertificateDocument
        {
            get { return (System.String)GetValue((int)ProductFieldIndex.CertificateDocument, true); }
            set { SetValue((int)ProductFieldIndex.CertificateDocument, value); }
        }

        /// <summary> The Created property of the Entity Product<br/><br/></summary>
        /// <remarks>Mapped on  table field: "Product"."Created"<br/>
        /// Table field type characteristics (type, precision, scale, length): NVarChar, 0, 0, 2147483647<br/>
        /// Table field behavior characteristics (is nullable, is PK, is identity): true, false, false</remarks>
        [DataMember]
        public virtual System.String Created
        {
            get { return (System.String)GetValue((int)ProductFieldIndex.Created, true); }
            set { SetValue((int)ProductFieldIndex.Created, value); }
        }


Anything I'm doing wrong?

Hope you can help.

Greetz, Stefan

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39625
Joined: 17-Aug-2003
# Posted on: 21-Nov-2013 13:43:28   

Still looks like the json serializer is misconfigured at the moment serialization takes place or another one is used instead of the newtonsoft one. Don't you have a WebApiConfig.cs ? What it should do is not use the ISerializable interface (which it clearly does in your json output), but use the IXmlSerializable interface which produces the right properties and values for this (as the XML is the same as json in this case: per field an element, no other stuff, exactly what you expected).

So as it still uses the ISerializable interface, it doesn't produce the json you'd expect. Please check (if possible) whether the ContractResolver settings are as they should be when the entity is serialized to json

Frans Bouma | Lead developer LLBLGen Pro
superska avatar
superska
User
Posts: 35
Joined: 11-Nov-2013
# Posted on: 22-Nov-2013 01:51:20   

Otis wrote:

So as it still uses the ISerializable interface, it doesn't produce the json you'd expect. Please check (if possible) whether the ContractResolver settings are as they should be when the entity is serialized to json

Thanks,. Frans. Your explanation really helped me on the right track. Appearently, I need to explicitly pass the right MediaTypeFormatter to the Request.CreateResponse method. I'll probably be able to make the code a bit more efficient, but at least it's working. You gotta start somewhere, right?

Greetz, Stefan

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39625
Joined: 17-Aug-2003
# Posted on: 22-Nov-2013 11:44:16   

Glad it's working! simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Posts: 6
Joined: 06-Mar-2014
# Posted on: 06-Mar-2014 07:47:38   

superska wrote:

Otis wrote:

So as it still uses the ISerializable interface, it doesn't produce the json you'd expect. Please check (if possible) whether the ContractResolver settings are as they should be when the entity is serialized to json

Thanks,. Frans. Your explanation really helped me on the right track. Appearently, I need to explicitly pass the right MediaTypeFormatter to the Request.CreateResponse method. I'll probably be able to make the code a bit more efficient, but at least it's working. You gotta start somewhere, right?

Greetz, Stefan

Hi Stefan,

Do you mind posting the code that solved your problem? I have exactly the same issue and have not been able to work out what this means:


Appearently, I need to explicitly pass the right MediaTypeFormatter to the Request.CreateResponse method

Regards, Andrew

superska avatar
superska
User
Posts: 35
Joined: 11-Nov-2013
# Posted on: 06-Mar-2014 09:30:23   

AndrewGalea wrote:

Hi Stefan,

Do you mind posting the code that solved your problem? I have exactly the same issue and have not been able to work out what this means:


Appearently, I need to explicitly pass the right MediaTypeFormatter to the Request.CreateResponse method

Regards, Andrew

No problem. Here goes: I have a helper method to create the MediaTypeFormatter


        private JsonMediaTypeFormatter _jsonFormatter = null;
        protected MediaTypeFormatter GetFormatter()
        {
            if (_jsonFormatter == null)
            {
                // initialize json formatter
                _jsonFormatter = new HttpConfiguration().Formatters.JsonFormatter;
                _jsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
                _jsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver()
                {
                    IgnoreSerializableInterface = true,
                    IgnoreSerializableAttribute = true
                };
            }
            return _jsonFormatter;
        }

Then, I have this call to create the response message. Request is System.Web.Http.ApiController.Request:


            return Request.CreateResponse(HttpStatusCode.OK, new ArrayList(){
                new ProductEntity(){ProductNo = "123456"}
            }, GetFormatter());

Hope this helps.

Greetz, Stefan

Posts: 6
Joined: 06-Mar-2014
# Posted on: 06-Mar-2014 22:15:35   

Thanks Stefan. I actually got it working shortly after posting on your thread. I used the exact same solution although I had not moved the JSON serialization code to its own method, which is nicer.

Thanks for taking the time to post. simple_smile

Regards, Andrew

superska avatar
superska
User
Posts: 35
Joined: 11-Nov-2013
# Posted on: 06-Mar-2014 22:43:17   

AndrewGalea wrote:

Thanks Stefan. I actually got it working shortly after posting on your thread. I used the exact same solution although I had not moved the JSON serialization code to its own method, which is nicer.

Thanks for taking the time to post. simple_smile

Regards, Andrew

Good o hear. And no problem. The thread is more complete this way :-)

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 07-Mar-2014 06:57:43   

Thanks for the feedback Stefan wink

David Elizondo | LLBLGen Support Team
MichelZ avatar
MichelZ
User
Posts: 24
Joined: 25-Jul-2008
# Posted on: 04-Jan-2015 14:58:24   

I resolved this by removing the other formatters. I don't know if it has any other side effects yet:

        // Web API configuration and services
        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
        json.SerializerSettings.ContractResolver = new DefaultContractResolver()
        {
            IgnoreSerializableInterface = true,
            IgnoreSerializableAttribute = true
        }; 

** config.Formatters.Clear(); config.Formatters.Add(json);**