JSON

3 posts

JOS.ContentSerializer 3.0 has been released!

New features

Support for more built in properties

The following properties are now supported out of the box:

  • BlockData
  • bool
  • double
  • ContentArea
  • ContentReference
  • DateTime
  • IEnumerable<ContentReference>
  • int
  • LinkItemCollection
  • PageReference
  • PageType
  • string[]
  • string SelectOne/SelectMany support.
  • Url
  • XhtmlString
  • NEW IEnumerable<string> ICollection/IList works as well
  • NEW IEnumerable<int> ICollection/IList works as well
  • NEW IEnumerable<double> ICollection/IList works as well
  • NEW IEnumerable<DateTime> ICollection/IList works as well

Easier to add support for custom properties

The old solution to add support for custom properties sucked. I've rebuilt it from scratch.

Example:
You're using the property Jos.PropertyKeyValueList on your StartPage like this.

public class StartPage : PageData  
{
    ...
    public virtual string Heading { get; set; }
    public virtual IEnumerable<KeyValueItem> KeyValueItems{ get; set; }
    ...
}

Now, if you call .ToJson on a StartPage instance you would only get the Heading property in the json output since IEnumerable<KeyValueItem> isn't handled out of the box.

{
    "heading" : "Where is my KeyValueItems??"
}

To add support for it, first create a new class that implements the IPropertyHandler<> interface

public class KeyValueItemListPropertyHandler : IPropertyHandler<IEnumerable<KeyValueItem>>  
{
    public object Handle(IEnumerable<KeyValueItem> value, PropertyInfo property, IContentData contentData)
    {
        // Do whatever you want with the property here.
        return value;
    }
}

Then register your class in your DI container.
Example

[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class MyConfigurationModule : IConfigurableModule  
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    { 
    context.Services.AddSingleton<IPropertyHandler<IEnumerable<KeyValueItem>>, KeyValueItemListPropertyHandler>();
    }
}

Now, if you would call .ToJson again on your StartPage instance, you would see the following output.

{
    "heading": "Where is my KeyValueItems??",
    "keyValueItems": [
        {
            "key": "Some key",
            "value": "Hello there"
        },
        {
            "key": "Another key",
            "value": "Another value!"
        }
    ]
}

Easier to extend/replace built in PropertyHandlers

Say that you, for some reason, want all strings to return "JOSEF OTTOSSON!!" instead of their actual value.

Just create a new propertyhandler for strings like this.

public class JosefStringPropertyHandler : IPropertyHandler<string>  
{
    public object Handle(string value, PropertyInfo property, IContentData contentData)
    {
        return "JOSEF OTTOSSON!!";
    }
}

Then swap out the default StringPropertyHandler in the DI container like this:

context.Services.AddSingleton<IPropertyHandler<string>, JosefStringPropertyHandler>();  

Custom PropertyHandler for specific property

It's possible to specify which PropertyHandler to use by adding the ContentSerializerPropertyHandlerAttribute on the desired property.
NOTE, this is currently an experimental feature, it works, but I've had no time to write tests yet, so consider it a BETA until further notice ;)

Added a __type__ property to contentarea items

I've added a property to all contentarea items named __type__.
It will be added automatically to the json output. It looks like this:

"mainContentArea": {
    "jumbotronBlock": [{
        ....
        "heading": "Wherever you meet!",
        "__type__": "JumbotronBlock"
        ....
    }],
    "teaserBlock": [{
    ....
    "heading": "Alloy Plan",
    "__type__": "TeaserBlock"
    ....
    }]
},

It's possible to change the property name by setting the BlockTypePropertyName(just realised that that name sucks, will fix it in the next release) property in the IContentSerializerSettings.

Breaking changes

  • All PropertyHandler interfaces are gone(IStringPropertyHandler, IStringArrayPropertyHandler and so on). Use IPropertyHandler<> instead.
  • ContentReferenceSettings has been removed, use IUrlSettings instead.
  • Added MIT License.
  • ContentAreaPropertyHandler now uses .FilteredItems instead of .Items.

As always, all code can be found on Github

EPiServer ContentData to JSON

pug with puppies

Note, the project has seen a major update(even more awesomeness), you can read more at GitHub or here

When working with EPiServer you would normally use standard Razor views to render your frontend code...you will get syntax support in Visual Studio and can use many nice little HtmlHelpers, I love it. But...im a backend developer. The frontend people at my work hates Visual Studio. They are also using some cool sounding tools like Backbone and Handlebars so they don't want to use ordinary Razor views...

In another blog post I showed how we transfered the EPiServer properties to our frontend templates so Im not going to show that again, but basically we are dumping a javascript object in the DOM and then our frontend framework picks it up and generates the markup.

So...how are we generating the JSON?

First I built a thing that took all items in a ContentArea, filtered out all items that had a specific attribute on their Class and inherited from a specific Class, then I selected all properties in the class that had a specific attribute. It worked but it was very annoying to set it up to say the least. One more huge annoyance was that it didn't supported ContentAreas and nested Internalblocks.

So this weekend I rebuilt everything. My goal was to get rid of the inheritance/attribute-mayhem, support nested blocks/contentAreas(recursive) and make it easy for developers to use it.

I think I've succeeded :)

Currently the following property types are supported:

  • String
  • bool
  • XhtmlString
  • ContentArea (different ContentTypes as well!)
  • InternalBlock (using Blocks as a property)
  • Double
  • Int
  • DateTime
  • SelectOne
  • SelectMany
  • PageReference
  • ContentReference
  • LinkItemCollection
  • Url

Let me show you guys how it can be used:

public class StartpageController : PageController<Startpage>  
{
    public string Index(Startpage currentPage)
    {
        Response.ContentType = "application/json";
        var json = currentPage.ToJson();
        return json;
    }
}

That's it! I've created an extension method to the ContentData-type so it works both on pages and blocks.

This is how my Startpage looks:

[ContentType(DisplayName = "Startpage", GUID = "a6762bfb-973b-41c1-acf8-7d26567cd71d", Description = "")]
    public class Startpage : PageData
    {
        [Display(Name = "String", Order = 100)]
        [JsonProperty("string")]
        public virtual string Heading { get; set; }

        [Display(Name = "XhtmlString", Order = 110)]
        [JsonProperty("xhtmlString")]
        public virtual XhtmlString HtmlText { get; set; }

        [Display(Name = "InternalBlock", Order = 160)]
        [JsonProperty("internalBlock")]
        public virtual InternalBlock InternalBlock { get; set; }

        [Display(Name = "Contentarea", Order = 170)]
        [JsonProperty("contentArea")]
        public virtual ContentArea ContentArea { get; set; }
    }

The only thing that needs to be added to make the property appear in the JSON output is the JsonProperty-attribute(Newtonsoft).

Im thinking about removing that and instead use the Display-attribute and make the JsonProperty-attribute optional if you want to specify a custom JSON key.

Note, the above is now implemented, you dont need the JsonProperty attribute. Read more at GitHub. EPiServer - ContentJson

The JSON genereated would look like this:

{
        "heading": "This is the heading",
        "body": "<p>This is cool because <strong>JOSEF WROTE IT</strong></p>",
        "internalBlock": {
            "heading" : "Internalblock Heading here",
            "subHeading" : "This is my subheading"
        },
        "contentArea": {
            "sharedBlockName" : [
                {
                    "heading" : "Hello",
                    "subHeading" : "Hey you"
                },
                {
                    "heading" : "Goodbye",
                    "subHeading" : "Cheers"
                }
            ],
            "rappersBlock" : [
                {
                    "name" : "Eminem",
                    "isTheGreatestRapperEver": true,
                    "sucks" : false
                },
                {
                    "name" : "Big Sean",
                    "isTheGreatestRapperEver" : false,
                    "sucks" : false
                },
                {
                    "name" : "Flo Rida",
                    "isTheGreatestRapperEver" : false,
                    "sucks" : true
                }
            ]
        }
    }

That's just a simple example, head over to the GitHub repo and look at some other examples and try it out!

Don't forget to look at how I solved the SelectOne/SelectMany property type, I think it turned out quite nicely

XForms support

Yeah...im thinking about it, but I will not implement it right now, it will come, someday. Right now I just don't feel like parsing XML... :)