1

I have Updated the Question, Please see under Update 1

I am getting an error when my WebAPI tries to return the data in XML format. The JSON format works fine.

Error Details :

The 'ObjectContent`1' type failed to serialize the response body for content type

'application/xml; charset=utf-8 '.

  • Detailed Error ::

Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'Newtonsoft.Json.Linq.JToken' to remove references to itself.

I somehow understood that its due to sending the deserialised object from Json.Net as the result,

return (JsonConvert.DeserializeObject(JsonString, new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore

            }));

The Problem here is that I cant create one more model class as the results will be dynamic based on the user field selection (I am already using one model class) to send the results to the user.

We want to support both XML and JSON formats and have already tried the most of the solutions mentioned in StackOverflow. Any help will be appreciated.

Update 1

I have tried to implement the solution mentioned in the link

Circular reference exception when serializing an object containing a JToken to XML in Web API

Still It shows the same error.

To make Sure I am doing the correct thing I am posting the code here.

I am getting the Jobject here.

        object test = JsonHandling.generateResultSet<IEnumerable<Mytype>>
        (reader,"dsfw");

This is what is doing :

    public static object generateResultSet<T>(T resultList, string s)
    {
        try
        {
            string JsonString = JsonConvert.SerializeObject(resultList, new JsonSerializerSettings
            {
                // Formatting=Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore,
            });

            // return (JsonHelper.Deserialize(JsonString));

            return (JsonConvert.DeserializeObject(JsonString, new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore

            }));
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

Here is my Final Result Set Class Post the Modifications mentioned in the solution

public class PagedResults<T>
{
    /// <summary>
    /// The page number this page represents.
    /// </summary>
    /// 
    public int? OffSet { get; set; }

    /// <summary>
    /// The size of this page.
    /// </summary>
    public int? Limit { get; set; }

  //  /// <summary>
  //  /// The total number of pages available.
  //  /// </summary>
  //// public int TotalNumberOfPages { get; set; }

    /// <summary>
    /// The total number of records available.
    /// </summary>
    public int TotalNumberOfRecords { get; set; }

    /// <summary>
    /// The URL to the next page - if null, there are no more pages.
    /// </summary>
    public string NextPageUrl { get; set; }

    /// <summary>
    /// The URL to the Previous page - if null, there are no more pages.
    /// </summary>
    public string PrevPageUrl { get; set; }

    /// <summary>
    /// The URL to the Current page page - if null, there are no more pages.
    /// </summary>
    public string CurrentPageUrl { get; set; }

    /// <summary>
    /// The records this page represents.
    /// </summary>
    [XmlIgnore]
    [JsonProperty]
    public object Results { get; set; }

    [XmlAnyElement("Results")]
    [JsonIgnore]
    public XElement ResultsXml
    {
        get
        {
            return JsonExtensions.SerializeExtraDataXElement("Results", Results);
        }
        set
        {
            Results = JsonExtensions.DeserializeExtraDataXElement("Results", value);
        }
    }
    /// <summary>
    /// This Method creates the paged result set 
    /// </summary>
    /// <typeparam name="TReturn">Type of the model class that has been passed</typeparam>
    /// <param name="results">Reults returned by the DB</param>
    /// <param name="offset">Rows to offset </param>
    /// <param name="limit">Total number of the rows</param>
    /// <param name="req">HTTPrequest obejct </param>
    /// <param name="routeName">The Name of the rouote from the route collections</param>
    /// <returns></returns>
    public PagedResults<TReturn> CreatePagedResults<TReturn>(object results,int? offset,int? limit,HttpRequestMessage req,string routeName)
    {
        try
        {
            var skipAmount = offset;
            var totalNumberOfRecords = 1000;
            var urlHelper = new UrlHelper(req);
            var prevLink = offset > 0 ? urlHelper.Link(routeName, new { offset = offset - 1, limit = limit }) : "";
            var nextLink = (offset + limit) < totalNumberOfRecords - 1 ? urlHelper.Link(routeName, new { offset = offset + limit, limit = limit }) : "";
            var currLink = urlHelper.Link(routeName, new { offset = offset, limit = limit });
            return new PagedResults<TReturn>
            {
                Results = results,
                OffSet = offset,
                Limit = limit,
                TotalNumberOfRecords = totalNumberOfRecords,
                NextPageUrl = nextLink,
                PrevPageUrl = prevLink,
                CurrentPageUrl = currLink
            };
        }
        catch(Exception ex)
        {
            throw ex;
        }
    }
}

If I Comment

       Results = results It works but thats what want to include in result.

Final Return :

PagedResults<MyType> result = new PagedResults<MyType>();
      result = result.CreatePagedResults<MyType>(test, offset, limit, 
Request, "jkewgfewg");
         return Ok(result);

I have added the JsonExtensions as mentioned there

dbc
  • 104,963
  • 20
  • 228
  • 340
harish
  • 35
  • 5
  • Error message is from newtonsoft.json but your complaint is that your xml is not well formed? This seems like a mismatch to me; are you using json or xml? Is your query missing some detail that will help us resolve? – Caius Jard Jul 31 '17 at 23:14
  • Where are you getting the JsonString from in the first place? – Brian Rogers Jul 31 '17 at 23:17
  • @BrianRogers : I am getting the Json String from my Model Object. The Reason to use Newtonsoft is to ignore the columns that holds null. string JsonString = JsonConvert.SerializeObject(resultList, new JsonSerializerSettings { // Formatting=Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, }); – harish Jul 31 '17 at 23:20
  • @CaiusJard The error message comes from newtonsoft.json when I am requesting my format to be XML. if you see the it tells that there is recursive reference on the 'Newtonsoft.Json.Linq.JToken' thats why it is unable to convert it XML. As my final return that returns the data to use is from Newtonsoft.json you are seeing it in detailed error . Let me know if further clarification is needed – harish Jul 31 '17 at 23:24
  • It sounds to me like the problem is the XML serializer doesn't know how to handle serializing a `JToken`. Try using the helper method from [this answer](https://stackoverflow.com/a/19140420/10263) to turn the JSON string into plain .NET objects, then return that result from your web API method: `return JsonHelper.Deserialize(JsonString);` – Brian Rogers Jul 31 '17 at 23:34
  • @BrianRogers Thanks for the quick reponse. I am pretty new to this. I tried to implement what was mentioned in the Link. It shows me this error. (contd..) – harish Jul 31 '17 at 23:42
  • (contd..) Type 'System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' 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 – harish Jul 31 '17 at 23:42
  • Oh that's right. I always forget that XmlSerializer can't handle dictionaries either. Never mind then. – Brian Rogers Jul 31 '17 at 23:55
  • 1
    Looks like a duplicate of [Circular reference exception when serializing an object containing a JToken to XML in Web API](https://stackoverflow.com/q/40978662). – dbc Aug 01 '17 at 00:34
  • @dbc let me chk on that approach and will let you know. – harish Aug 01 '17 at 00:49
  • @dbc I have updated the question can you please help now, – harish Aug 01 '17 at 18:34
  • @harish - In that case we will need to see a [mcve] to help you. Your code doesn't even compile because `MyType` is undefined. That being said, your `PagedResults` type is very peculiar. It is a generic type, but the generic parameter `T` **is never used**. Why is it generic at all? If `Results` is actually supposed to be of type `T` why not just do `public T Results { get; set; }` and let the framework automatically serialize it as either JSON or XML? – dbc Aug 01 '17 at 22:30

1 Answers1

0

The reason the answer from Circular reference exception when serializing an object containing a JToken to XML in Web API is not working for you us that you are using DataContractSerializer while that answer works for XmlSerializer. You have a few options to resolve this problem.

Firstly, you could switch to XmlSerializer globally as explained here, or for a specific controller as explained here, or for a specific response as shown here or here.

Secondly, you could annotate your PagedResults<T> with data contract attributes to ensure that the JObject-valued property is not serialized:

[DataContract]
public class PagedResults<T>
{
    /// <summary>
    /// The page number this page represents.
    /// </summary>
    /// 
    [DataMember]
    public int? OffSet { get; set; }

    /// <summary>
    /// The size of this page.
    /// </summary>
    [DataMember]
    public int? Limit { get; set; }

    //  /// <summary>
    //  /// The total number of pages available.
    //  /// </summary>
    //// public int TotalNumberOfPages { get; set; }

    /// <summary>
    /// The total number of records available.
    /// </summary>
    [DataMember]
    public int TotalNumberOfRecords { get; set; }

    /// <summary>
    /// The URL to the next page - if null, there are no more pages.
    /// </summary>
    [DataMember]
    public string NextPageUrl { get; set; }

    /// <summary>
    /// The URL to the Previous page - if null, there are no more pages.
    /// </summary>
    [DataMember]
    public string PrevPageUrl { get; set; }

    /// <summary>
    /// The URL to the Current page page - if null, there are no more pages.
    /// </summary>
    [DataMember]
    public string CurrentPageUrl { get; set; }

    /// <summary>
    /// The records this page represents.
    /// </summary>
    [XmlIgnore]
    [IgnoreDataMember]
    [JsonProperty]
    public object Results { get; set; }

    [XmlAnyElement("Results")]
    [DataMember(Name = "Results")]
    [JsonIgnore]
    public XElement ResultsXml
    {
        get
        {
            return JsonExtensions.SerializeExtraDataXElement("Results", Results);
        }
        set
        {
            Results = JsonExtensions.DeserializeExtraDataXElement("Results", value);
        }
    }
}

However, there is an issue with this implementation that arises because DataContractSerializer does not support functionality precisely equivalent to [XmlAnyElement], as explained here. Serializing to XML with this serializer will introduce an extra level of XML element nesting not present when using XmlSerializer (or Json.NET). For instance, consider the following definition of MyType:

public class MyType
{
    public string Foo { get; set; }
    public string Bar { get; set; }
}

Serializing a PagedResult<MyType> with each serializer gives the following results:

  • Json.NET:

    {    
      "OffSet": 0,
      "Limit": 1000,
      "TotalNumberOfRecords": 1000,
      "NextPageUrl": "next link",
      "PrevPageUrl": "prev link",
      "CurrentPageUrl": "curr link",
      "Results": {
        "Foo": "Foo Value",
        "Bar": "Bar Value"
      }
    }
    
  • XmlSerializer:

    <PagedResultsOfMyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <OffSet>0</OffSet>
      <Limit>1000</Limit>
      <TotalNumberOfRecords>1000</TotalNumberOfRecords>
      <NextPageUrl>next link</NextPageUrl>
      <PrevPageUrl>prev link</PrevPageUrl>
      <CurrentPageUrl>curr link</CurrentPageUrl>
      <Results>
        <Foo>Foo Value</Foo>
        <Bar>Bar Value</Bar>
      </Results>
    </PagedResultsOfMyType>
    
  • DataContractSerializer:

    <PagedResultsOfMyType5vmUPXwL xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Question45426622">
      <CurrentPageUrl>curr link</CurrentPageUrl>
      <Limit>1000</Limit>
      <NextPageUrl>next link</NextPageUrl>
      <OffSet>0</OffSet>
      <PrevPageUrl>prev link</PrevPageUrl>
      <Results>
        <!--Notice the nested extra <Results> node below -->
        <Results xmlns="">
          <Foo>Foo Value</Foo>
          <Bar>Bar Value</Bar>
        </Results>
      </Results>
      <TotalNumberOfRecords>1000</TotalNumberOfRecords>
    </PagedResultsOfMyType5vmUPXwL>
    

Notice the nested, added level of <Results> node? This cannot be avoided with DataContractSerializer unless you manually implement IXmlSerializer for your entire PagedResult<T> object, which I do not recommend since it is quite difficult to do correctly.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thanks for the details explanation. This actually me a lot and the XMLserliazer works. This is what I was looking for. – harish Aug 02 '17 at 19:04