3

I'm trying to make a generic handler post a JSONJ object based on my entity type SYSTEM_AUDIT_SHEET:

SYSTEM_AUDIT_SHEET sheet = ctx.SYSTEM_AUDIT_SHEET
                              .Where(s => s.SYSTEM_KEY == system_key_dec)
                              .Select(s => s)
                              .OrderByDescending(s => s.AUDIT_SHEET_VERSION)
                              .First();

HttpContext.Current.Response.Write(serializer.Serialize(sheet));

But I get the following error:

A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.SYSTEM_AUDIT_SHEET_521A7B786A51FC0F83641182DD72C8DFE6C082418D30BBB977B403409A74CE17'.

Why can't I convert the entity to JSON?

Yuck
  • 49,664
  • 13
  • 105
  • 135
Johan
  • 35,120
  • 54
  • 178
  • 293
  • Create a new class similar to the object you want to serialize but without having circular reference. Fill the data in that class from sheet object and serialize it. It's a bad practice to serialize Entity Framework Object directly because of this sort of problem. – Birey Oct 24 '11 at 13:58
  • Duplicate question; answers are "use JSON.Net" which has several solutions to this problem without copy/pasting classes. http://stackoverflow.com/questions/4606232/circular-reference-exception-with-json-serialisation-with-mvc3-and-ef4-ctp5w – Chris Moschini Oct 24 '11 at 14:54

2 Answers2

1

You cannot convert objects to json that reference themselves as this would create an infinitely long json string.

For example, the following pseudo-code wouldn't work because it sets up a circular reference (Dog >> Bone >> Dog...):

class Dog {
    private Bone myBone;
    public Dog() {
        myBone = new Bone(this);
    }
}

class Bone {
    private Dog buriedBy;
    public Bone(Dog d) {
        buriedBy = d;
    }
}

There seem to be some good solutions by googling 'json circular reference'. See the top two stack overflow links.

jtfairbank
  • 2,311
  • 2
  • 21
  • 33
  • 3
    'Google It' is not a very helpful answer. Could you provide a link? – Chris Shouts Oct 24 '11 at 14:30
  • I provided an answer and a way of getting more material. There were multiple links I referenced on the search page... really giving a specific google search term was the most effective way of providing the additional reference material. – jtfairbank Oct 24 '11 at 15:04
  • 1
    Here are the links for those who need them: 1. [Json and Circular Reference Exception](http://stackoverflow.com/questions/2002940/json-and-circular-reference-exception) 2. [EF 4.1 - Code First - JSON Circular Reference Serialization Error](http://stackoverflow.com/questions/5588143/ef-4-1-code-first-json-circular-reference-serialization-error) 3. [Best solution for EF 4.1 + MVC + JSON circular reference exception?](http://stackoverflow.com/questions/6622430/best-solution-for-ef-4-1-mvc-json-circular-reference-exception) – Chris Shouts Oct 24 '11 at 16:55
1

The problem is probably that your SYSTEM_AUDIT_SHEET either contains a property that references instances of type SYSTEM_AUDIT_SHEET, or it contains a property that points to objects that have pointers to SYSTEM_AUDIT_SHEET instances. Serializing such a circle of pointers would result in a serialization process that would never end.

You will need to transform your SYSTEM_AUDIT_SHEET to a type that does not (directly or indirectly) reference itself before doing the serialization. You can create a brand new type and write code to instantiate such a type from your SYSTEM_AUDIT_SHEET (AutoMapper might come in handy here). However, I tend to find that in most cases it is easier to just use an anonymous type:

SYSTEM_AUDIT_SHEET sheet = /*some sheet*/
var json = new {
  sheet.Id,
  sheet.RevisionNumber,
  sheet.Title
};
return serializer.Serialize(json);

EDIT

If you want to use AutoMapper and assuming that your sheet looks something like

class SYSTEM_AUDIT_SHEET 
{
    public int Id { get; set; }
    public SYSTEM_AUDIT_SHEET SomeOtherAuditSheet { get;set;}
    public string Title { get;set;}
}

you could create a type like

class JSON_SYSTEM_AUDIT_SHEET
{
    public int Id { get; set; }
    public int SomeOtherAuditSheetsId { get;set;}
    public string Title { get;set;}
}

When your application starts (say, in Application_Start) you configure AutoMapper:

AutoMapper.Mapper.CreateMap<SYSTEM_AUDIT_SHEET, JSON_SYSTEM_AUDIT_SHEET>()
  .ForMember(dest => dest.SomeOtherAuditSheetsId, opt => opt.MapFrom(src => src.SomeOtherAuditSheet.Id));

The Id and Title properties will be mapped directly across from SYSTEM_AUDIT_SHEET to JSON_SYSTEM_AUDIT_SHEET because they have the same names in both types. The property SomeOtherAuditSheetsId needs special configuration, because there is no property with that exact name on the source type.

When you want to convert SYSTEM_AUDIT_SHEET to JSON_SYSTEM_AUDIT_SHEET you do:

return AutoMapper.Mapper.Map<SYSTEM_AUDIT_SHEET , JSON_SYSTEM_AUDIT_SHEET >(sheet);

You may want to have a look at AutoMapper's flattening features.

Hope this helps.

Rune
  • 8,340
  • 3
  • 34
  • 47
  • Yea but since there are like 30 properties i dont want to do that manually. I suppose thats what automapper is for? Could you show me how its used? – Johan Oct 24 '11 at 13:55
  • +1 for use of AutoMapper. Anyone aware of an existing tool or template that automatically duplicates a POCO for this? – Chris Moschini Oct 24 '11 at 14:41