0

Today I was playing around with the jsonb datatype in postgres, using ServiceStack Ormlite. A basic model had a complex type property that itself contained a dictionary of interface objects (pseudocode below).

Usually ServiceStack handles this by adding a "__type" identifier to the json. It did this correctly. However, for one of the dictionary items the "__type" identifier was listed second, below a property, and this caused a null object to be returned when the item was retrieved from postgres. What is more interesting is that, as best as I can tell, the serializer had the "__type" listed first but, once it was stored in postgres, it was re-ordered. Does a jsonb datatype re-order json properties?

Pseudocode of the model (it was more extensive in the real code)

public class StrategyContainer
{
    public Dictionary<int, List<IStrategy>> SetOfStrategies { get; set; }
}

public interface IStrategy
{
    int Health { get; set; }
    string Name { get; set; }
}

public interface IAttack : IStrategy
{
    int Strength { get; set; }
}

public abstract class AbstractStrategy : IStrategy
{
    public int Health { get; set; }
    public string Name { get; set; }
}
public class Attack : AbstractStrategy, IAttack
{
    public int Strength { get; set; }
}

And here is how the json appeared when retrieved from the postgres jsonb column.

{
"SetOfStrategies": {
    "1": [{
        "__type": "Test.Attack, Test",
        "Strength": 10,
        "Health": 5,
        "Name": "Testing"
    },
    {
        "Strength": 20,
        "__type": "Test.Attack, Test",
        "Health": 10,
        "Name": "Testing2"
    }]
}

Note: there were many others items in the dictionary list. Only one of them had the mistaken "__type". All the others loaded correctly.

For now I've moved back to a standard text column type and it all works fine.

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
biddrafter
  • 67
  • 4

2 Answers2

1

If you don't want __type info in the generated JSON, you shouldn't use interfaces or late-bound objects in Data models.

Especially when persisting across process boundaries (e.g. db, cache, network) I'd look at avoiding OOP object graphs and look at using clean concrete POCO models instead (which have more predictable behavior everywhere).

Community
  • 1
  • 1
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Thanks mythz. However, in this case I very much did want the type information because I was testing it specifically and I was surprised at the behavior in postgres, since it modified the ordering of the properties in jsonb. Ordinarily I agree with your idea of using only concrete models. However, I thought it worth documenting here since the "__type" approach is supported in SS and the behavior I saw could lead to quite unexpected outcomes. – biddrafter Aug 01 '15 at 23:25
1

jsonb stores json objects as dictionaries, so order of keys within an object is not preserved. Frankly, I think applications that assume a specific key ordering are somewhat broken, but they're certainly out there.

If you want to preserve key ordering (and preserve duplicate keys) you need to use the plain json type. This validates the json, but doesn't do anything else with it. The downside is that it has much more limited indexing and in-database operators.

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778