2

I'm building a web application with a mvc project with angularjs on one side and a Webapi on the other.

I will have a lot a views and don't want to create as many viewmodel variants on the webApi side.

Some view are list of items, some view are detail of one item.

I could create 2 viewmodels(light/summary and full/detail) for each business entity in my application but I would have many classes and it could be tricky for maintenance or evolution.

My idea was to create less viewmodels and have attributes to specify the properties I want. Light result json or a complete one.

Ok, It's obviously not clear. Please stay with me

public class BookVM
    {  
        [WebApiRef]
        public long Id { get; set; }

        [WebApiRef]
        public String Title { get; set; }

        public DateTime Date { get; set; }

        public float Price { get; set; }

        public AuthorVM Author { get; set; }
    }

    public class AuthorVM
    {  
        [WebApiRef]
        public long Id { get; set; }

        [WebApiRef]
        public String Name { get; set; }

        public int Age { get; set; }

        public String Country { get; set; }

        public List<BookVM> Books { get; set; }

        [...]
    }

For a page listing books I expect a list of serialized BookMV in json like so :

[
  {
    "Id": 1,
    "Title": "The Lord of the Rings",
    "Date": "29\/07\/1954",
    "Price": "23",
    "Author": {
      "Id": 854,
      "Name": "J. R. R. Tolkien"
    }
  },
  {
    "Id": 2,
    "Title": "The Hobbit",
    "Date": "21\/08\/1937",
    "Price": "12",
    "Author": {
      "Id": 854,
      "Name": "J. R. R. Tolkien"
    }
  },
  {
    "Id": 3,
    "Title": "A Game of Thrones",
    "Date": "06\/08\/1996",
    "Price": "17",
    "Author": {
      "Id": 157,
      "Name": "George R. R. Martin"
    }
  }
]

for a page listing authors :

    [
  {
    "Id": 854,
    "Name": "J. R. R. Tolkien",
    "books": [
      {
        "Id": 1,
        "Title": "The Lord of the Rings"
      },
      {
        "Id": 2,
        "Title": "The Hobit"
      }
    ]
  },
  {
    "Id": 157,
    "Name": "George R. R. Martin",
    "books": [
      {
        "Id": 3,
        "Title": "A Game of Thrones"
      }
    ]
  }
]

for a page of an author :

    {
  "Id": 157,
  "Name": "George R. R. Martin",
  "books": [
    {
      "Id": 3,
      "Title": "A Game of Thrones"
    }
  ]
}

The attribute [WebApiRef] would define the properties to include in the attached entity but would not be used on the main type.

So depending on the type being serialized/deserialized, the attribute would be use to define what to keep in the attached entity inside.

I hope this make sense...

And my question is what I should do to acheive this ?

I read about Formatters, ContractResolvers, I'm not sure where or what to overloads.

Jonathan
  • 31
  • 2
  • Something along these lines, only with conditional ignoring of properties? [JsonProperty WebApi request and response models](https://stackoverflow.com/questions/34362628/jsonproperty-webapi-request-and-response-models/34365216#34365216)? – dbc Dec 29 '15 at 20:52
  • 1
    Thank you. This link helped me. I will post what I did once finished. – Jonathan Jan 04 '16 at 15:55

1 Answers1

0

In fact, It doesn't work.

I thought I could create a custom ContractResolver and apply my serialization logic there.

The problem was how to determine what is the type of the object I wanted to serialize.

I thought, I could save in the resolver the first type that is beeing passed by CreateProperty.

public class CustomSerializationContractResolver : DefaultContractResolver
{
    private Type _serializingType;

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        if (_serializingType == null)
        {
            _serializingType = member.DeclaringType;
        }

        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (_serializingType == member.DeclaringType)
        {
            property.ShouldSerialize = (instance) => true;
        }
        else if(Attribute.IsDefined(member, typeof(WebApiRefAttribute)))
        {
            property.ShouldSerialize = (instance) => true;
        }
        else
        {
            property.ShouldSerialize = (instance) => false;
        }


        return property;

    }
}

That worked but the contract resolver is not instanciated for each object serialization. So the saved type was incorrect for the next serialization.

I'm using WebApi and the contractResolver is set globally in WebApiConfig.cs It's shared accross all controller methods.

formatter.SerializerSettings.ContractResolver = new CustomSerializationContractResolver();

I found you can apply a ContractResolver by controller but the problem remains because you can have different return type by controller.

I didn't find a way to specify a ContractResolver by method unfortunately.

To conclude, it doesn't work because the custom ContractResolver is instanciated once and use :

  • by all methods (if set globally)
  • or use by every methods in controller (if set by controller)
Jonathan
  • 31
  • 2