0

I just find out the hard way that JSON is case sensitive which means that lWeekRecord['TT_WK_ID'] is not the same as lWeekRecord['tt_wk_id'] (lWeekRecord is an ISuperObject).

I cannot find anything about case sensitivity in the limited SuperObject documentation.

Has anyone ever found/fixed/hacked this; and can tell me how to do that?
I prefer my webservice that receives JSON data to be insensitive to JSON name case.

Edited:

I only need to consume JSON generated by another system, and that only talks to me. I want to be lenient towards the developers of that system. Their JSON is serialized C objects, and I really don't care if they call their vars foo or Foo. Actually, it's worse: parts of their objects are derived from database field names supplied to them, and these even come from different RDBMSes. So this is a chain of dependencies with multiple players involved that can break on something silly as case sensitivity.
If I can avoid that for those people, I want to.
No one in their right mind in that chain is going to use both foo and Foo together (and that would not even be possible in mosts RDMBSes), so I don't have to guard against that.

Community
  • 1
  • 1
Jan Doggen
  • 8,799
  • 13
  • 70
  • 144
  • `lWeekRecord['tt_wk_id']` this approach is not considered most speed-efficient and sometimes may lead to undocumented behaviour: http://github.com/hgourvest/superobject/issues/8 – Arioch 'The Jan 11 '16 at 15:56
  • Does it have to be case sensitive about the data passed ? can you just uppercase or lowercase ALL the json payload string, both names and values, before parsing ? – Arioch 'The Jan 11 '16 at 15:58
  • 1
    Don't impose Pascal semantics on JSON. :-) – Warren P Jan 11 '16 at 16:03
  • is your webservice alredy mostly written, or it is only starting? might you consider other libs? I find this in mORMot WebServices/JSON library: `TJSONTable = class(TObject) .... Handle a JSON result table, as returned by mORMot's server ... function FieldIndex(const FieldName: string): integer; ... Case-insensitive search for a field name` – Arioch 'The Jan 11 '16 at 16:03
  • since `IDispatch` is typically case-insensitive this maybe might work too (though I wonder if that is safe) `var V: variant; begin V := TDocVariant.NewJSON('{"id":10,"doc":{"name":"John","birthyear":1972}}'); assert(V.id=10); assert(V.doc.name='John');` and also `TDocVariantOption = ( dvoNameCaseSensitive, dvoCheckForDuplicatedNames....` – Arioch 'The Jan 11 '16 at 16:08
  • @Arioch'The Thanks for the warning, my names are all space-less. I'll leave a note in the code. – Jan Doggen Jan 11 '16 at 18:45
  • @Arioch'The I cannot just lowercase the payload, the data is indeed case sensitive (text strings among other things) – Jan Doggen Jan 11 '16 at 18:46
  • @Arioch'The Switching to another lib is indeed too late now. – Jan Doggen Jan 11 '16 at 18:46
  • Switching to another lib is pointless. They'll all behave the same way. Anyone who produces JSON will expect case to matter. You really are thinking about this the wrong way. – David Heffernan Jan 11 '16 at 19:41
  • @JanDoggen the problem is that space is only the surfaced gotcha, and who knows how many are still hidden within.... – Arioch 'The Jan 11 '16 at 20:01
  • @DavidHeffernan as you can see above, not all are the same, also there should be SAX-style parsers – Arioch 'The Jan 11 '16 at 20:10
  • @Arioch JSON is case sensitive. So all parsers will respect that. If they don't they aren't JSON parsers. – David Heffernan Jan 11 '16 at 20:33
  • @DavidHeffernan they should respect by default but might have extended options. Also for SAX-parsers the case sensitivity has nothing to do about the parser itself – Arioch 'The Jan 11 '16 at 21:06
  • @Arioch It's not JSON if it's not case sensitive. – David Heffernan Jan 11 '16 at 21:16
  • I now realize that my title *Make SuperObject library case-insensitive* sounded like a feature request. I did not mean that SuperObject *has to be* case sensitive, because obviously JSON *is* (and can we please stop all the discussion about that, we all know it is). Editing the title. – Jan Doggen Jan 12 '16 at 07:27
  • The consumers of your service will have to use the correct names. They can't use foo in place of bar. Why will it be so hard for them to also get the letter case right. The entire world of web services runs on top of case sensitive JSON. Why do you think that the consumers of your service will be not be able to manage? @Warren was spot on. This smacks of Pascal leakage. All that aside, even though what you propose is a bad idea, you now know how to do it. What exactly is the problem? – David Heffernan Jan 12 '16 at 08:28
  • you can also set an intermediate service, written with other JSON libraries or SuperObject itself, that would be a proxy normalizing names. Some SAX-style parser would be ideal I think. – Arioch 'The Jan 12 '16 at 14:45
  • @Arioch'The Is it really that hard to get the names right from the start? – David Heffernan Jan 12 '16 at 15:43
  • In the ideal world, where there is no human error, software bugs, broken machines and flip-flop memory cells that would be pretty damn easy. But here we see "this is a chain of dependencies with multiple players involved" situation. // Well, just how many WWW browsers only accept perfectly DTD-compliant XHTML and refuse to render free-form non-strict HTML ? – Arioch 'The Jan 12 '16 at 16:14
  • @Arioch'The What utter nonsense. – David Heffernan Jan 12 '16 at 17:15
  • Object property names **are case-sensitiive**. – Free Consulting Jan 12 '16 at 18:33

2 Answers2

3

This isn't really a SuperObject issue. Rather this is a property of JSON itself, which use case-sensitive comparisons.

If you really want to allow your service to be insensitive to letter case, then you'll need to implement the key lookup yourself. Instead of looking up a named value in an object, iterate over all the name/value pairs and perform whatever comparison you wish to do. In pseudo-code it would be something like this:

value := nil;
for name in obj do
  if SameText(name, targetName) then
  begin
    value := obj[name];
    break;
  end;

I would strongly urge you not to do this though. JSON is case sensitive. That's how it is designed and you will find life easier if you go with the flow. Swimming against the tide in this way will surely lead to all sorts of trouble later.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • David, your `value := obj[name];` may sometimes be problematic - https://github.com/hgourvest/superobject/issues/8 – Arioch 'The Jan 11 '16 at 15:51
  • 1
    @Arioch'The It is pseudo code, so I don't see any problems. I'm sure any reader can understand the intent of the pseudo code snippet. – David Heffernan Jan 11 '16 at 15:52
  • 3
    You know, pseudo-code tends to be considered a textbook and copy-pasted. Also let my comment be a warning of gotcha missing from the readme. – Arioch 'The Jan 11 '16 at 15:53
3

If you have two independent systems talking to each other you need a contract for each message. Just take this sample message:

{
  "foo": "value"
}

In Delphi the names are not case-sensitive but on serialization it does matter.

TMessage = class
  Foo: string;
end;

will generate on serialization

{
  "Foo": "value"
}

which is wrong by the contract.

Flag the field with an attribute to fullfill the contract on serialization

TMessage = class
private
  [SOName('foo')]
  FFoo: string;
public
  property Foo: string read FFoo write FFoo;
end;

Update

Using the attributes allows you the fullfill even worse contracts with mixing upper-lower-case names

TWorseMessage = class
private
  [SOName('foo')]
  FSmallFoo: string;
  [SOName('Foo')]
  FBigFoo: string;
public
  property SmallFoo: string read FSmallFoo write FSmallFoo;
  property BigFoo: string read FBigFoo write FBigFoo;
end;

It is only possible but you should not use it in real production.

Sir Rufo
  • 18,395
  • 2
  • 39
  • 73