episerver

13 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

JOS.ContentJson gets replaced by JOS.ContentSerializer

I've spent some time rebuilding the JOS.ContentJson library from scratch and when doing that I realised that it was kind of dumb to limit the library to only JSON. So I've added the possibility to replace the default serializer with a custom one, so you can return the data in any format you like(you have to build the serializer yourself though, I only provide a JSON serializer out of the box). By doing that, the name ContentJson looks kind of lame, so I've released a new package named JOS.ContentSerializer instead.

You can install it by doing Install-Package JOS.ContentSerializer
If you are using the JOS.ContentJson package, you will see that I've released a new major version, 3.0. I've marked many of the methods obsolete and I've also added a readme.txt that tells you to uninstall the package and install the new one instead.

One thing that's great(and bad) with this rewrite is that I could remove/refactor whatever I wanted so it's possible that I've removed something that you guys where using. I can't be sure since everything was based on extension methods in the old library which kind of makes it impossible for me to know which breaking changes I might have introduced, hence the new major version.

Another great thing is that the library now uses dependency injection everywhere, so it's extremely easy to customize the behavior. You can find the code and a more detailed README here.

If I've removed anything you were using, please create a new issue on GitHub and I will fix it asap.

Quick example

Consider the following pagetype:

public class DemoPage : PageData  
{
    [CultureSpecific]
    [Display(
        Name = "String",
        GroupName = SystemTabNames.Content,
        Order = 100)]
    public virtual string String { get; set; }

    [CultureSpecific]
    [Display(
        Name = "ContentArea",
        GroupName = SystemTabNames.Content,
        Order = 200)]
    public virtual ContentArea MainContentArea { get; set; }

    [CultureSpecific]
    [Display(
        Name = "Degrees",
        GroupName = SystemTabNames.Content,
        Order = 300)]
    public virtual double Degrees { get; set; }

    [CultureSpecific]
    [Display(
        Name = "Int",
        GroupName = SystemTabNames.Content,
        Order = 400)]
    public virtual int Int { get; set; }

    [CultureSpecific]
    [Display(
        Name = "Date",
        GroupName = SystemTabNames.Content,
        Order = 500)]
    public virtual DateTime DateTime { get; set; }

    [CultureSpecific]
    [Display(
        Name = "Bool",
        GroupName = SystemTabNames.Content,
        Order = 600)]
    public virtual bool Bool { get; set; }

    [CultureSpecific]
    [Display(
        Name = "PageType",
        GroupName = SystemTabNames.Content,
        Order = 700)]
    public virtual PageType PageType { get; set; }

    [CultureSpecific]
    [Display(
        Name = "ContentReference",
        GroupName = SystemTabNames.Content,
        Order = 800)]
    public virtual ContentReference ContentReference { get; set; }

    [CultureSpecific]
    [Display(
        Name = "PageReference",
        GroupName = SystemTabNames.Content,
        Order = 900)]
    public virtual PageReference PageReference { get; set; }

    [CultureSpecific]
    [Display(
        Name = "Url",
        GroupName = SystemTabNames.Content,
        Order = 1000)]
    public virtual Url Url { get; set; }

    [Display(
        Name = "InternalBlock",
        GroupName = SystemTabNames.Content,
        Order = 1100)]
    public virtual VimeoVideoBlock InternalBlock { get; set; }

    [Display(
        Name = "ContentReferenceList",
        GroupName = SystemTabNames.Content,
        Order = 1200)]
    public virtual IList<ContentReference> ContentReferenceList { get; set; }

    [Display(
        Name = "XhtmlString",
        GroupName = SystemTabNames.Content,
        Order = 1300)]
    public virtual XhtmlString XhtmlString { get; set; }

    [Display(
        Name = "LinkItemCollection",
        GroupName = SystemTabNames.Content,
        Order = 1400)]
    public virtual LinkItemCollection LinkItemCollection { get; set; }

    [Display(
        Name = "SelectOne",
        GroupName = SystemTabNames.Content,
        Order = 1500)]
    [SelectOne(SelectionFactoryType = typeof(ContactPageSelectionFactory))]
    public virtual string SelectOne { get; set; }

    [Display(
        Name = "SelectMany",
        GroupName = SystemTabNames.Content,
        Order = 1600)]
    [SelectMany(SelectionFactoryType = typeof(ContactPageSelectionFactory))]
    public virtual string SelectMany { get; set; }
}

By calling .ToJson on it like this

public class DemoPageController : PageController<DemoPage>  
{
    public string Index(DemoPage currentPage)
    {
        return currentPage.ToJson();
    }
}

You would get the following result

{
    "string": "This is a string",
    "mainContentArea": {
        "vimeoVideoBlock": [{
            "name": "My Vimeo Block"
        }]
    },
    "degrees": 133.7,
    "int": 1337,
    "dateTime": "2017-05-18T00:00:00+02:00",
    "bool": true,
    "pageType": "DemoPage",
    "contentReference": "http://localhost:52467/about-us/management/",
    "pageReference": "http://localhost:52467/alloy-plan/download-alloy-plan/",
    "url": "http://localhost:52467/globalassets/alloy-meet/alloymeet.png",
    "internalBlock": {
        "name": "Im a Vimeo block",
        "mainContentArea": {
            "youtubeVideoBlock": [{
                "name": "I am a youtube block in a contentarea on a Vimeoblock"
            }]
        }
    },
    "contentReferenceList": ["http://localhost:52467/search/", "http://localhost:52467/alloy-meet/"],
    "xhtmlString": "<p>I am a xhtmlstring, do you like it?</p>\n<p>Im <strong>bold not <em>bald</em></strong></p>",
    "linkItemCollection": [{
        "href": "http://localhost:52467/alloy-plan/?query=any",
        "title": "Any title",
        "target": "_blank",
        "text": "Any text"
    }, {
        "href": "https://josef.guru",
        "title": "External link",
        "target": "_blank",
        "text": "External"
    }, {
        "href": "mailto:[email protected]",
        "title": "Email link",
        "target": null,
        "text": "Email"
    }],
    "selectOne": [{
        "selected": false,
        "text": "Amar Gupta",
        "value": "34"
    }, {
        "selected": false,
        "text": "Fiona Miller",
        "value": "33"
    }, {
        "selected": true,
        "text": "Michelle Hernandez",
        "value": "30"
    }, {
        "selected": false,
        "text": "Robert Carlsson",
        "value": "32"
    }, {
        "selected": false,
        "text": "Todd Slayton",
        "value": "31"
    }],
    "selectMany": [{
        "selected": false,
        "text": "Amar Gupta",
        "value": "34"
    }, {
        "selected": true,
        "text": "Fiona Miller",
        "value": "33"
    }, {
        "selected": false,
        "text": "Michelle Hernandez",
        "value": "30"
    }, {
        "selected": false,
        "text": "Robert Carlsson",
        "value": "32"
    }, {
        "selected": false,
        "text": "Todd Slayton",
        "value": "31"
    }]
}