Added TdxJSONObject. It can be used in Delphi 5 - Delphi 10 Seattle+ Simple to convert regular Delphi objects to get ".AsJSON" support.

Added TdxJSONObject. It can be used in Delphi 5 - Delphi 10 Seattle+ Simple to convert regular Delphi objects to get ".AsJSON" support.
#Delphi10Seattle #opensource #MITLicense
https://github.com/darianmiller/dxLib
https://github.com/darianmiller/dxLib_Tests
IMHO the parser has some issues. For instance, older Delphi won't handle \u#### escapes as expected, since there is no conversion from UTF-16 codepoint to the string type. And I expect issues with numbers: it accepts 0123 as a valid JSON number (whereas it is not), and valid numbers like 1E-10 won't be recognized. Please ensure http://json.org specs are followed.
ReplyDeleteI don't know how far you got yet with your library. However, "dx" and "cx" prefixes are used by DevExpress. I do not claim anything and you may name as you like, but since they're huge in the Delphi world it might be a good move to change if possible to avoid confusion.
ReplyDeleteIf I got it right, in order to use JSON serialization, requirement is to descend from TdxJSONObject. That is totally wrong approach for implementing serialization framework.
ReplyDeleteFirst, any kind of tight coupling between business classes and serialization frameworks is bad. And coupling via inheritance is the worst kind of coupling.
ReplyDeleteBasic serialization rule is "If you need to add uses clause for serialization framework in your business class unit, you are doing it wrong."
With inheritance you are violating SRP principle. Class no longer does its primary role, but it also inherits knowledge of serialization format.
Also, if your class has to support different formats (XML), you bump into violating DRY and/or SRP again. Since class cannot inherit from two different classes you have to either add support for XML and all other serialization formats into base class - that cannot be called TdxJSONObject any more, or you have to create separate copy-paste business objects for each serialization format.
A. Bouchez I agree it could be more robust, but it has worked for all of the use cases that I've thrown at it over the last 3 years. I simply choose newer versions of Delphi to handle Unicode projects. In fact, the WideString/WideChar property types are not even supported in pre-2009. I just don't think the old versions are expected to robustly handle Unicode. If that's an expectation, then it's a false hope and I doubt I'd spend the time to expand it in this area. If others want to, I'd likely accept their additions...
ReplyDeleteDalija Prasnikar I refer to the Don't like it, Don't use it principle. : ) I'd much rather use this than something like SuperObject code: X.A['adresses'].O[1].S['adress'] := 'blabla'; I'd much rather use my approach than have strings littered throughout the code referring to field names which many implementations require. (SuperObject is the answer to a few StackOverflow questions on Delphi JSON.) I simply used it a few times before refusing to use it again and came up with my own approach.
ReplyDeleteDarian Miller I haven't used SuperObject so I cannot comment it. I expect from serialization framework to populate or store my business object. Not that I have to do that manually, or access some intermediately created object.
ReplyDeleteSo from that aspect, yes, you have working serialization, the problem is inheritance I mentioned before. You can achieve same ease of use without bringing inheritance in.
As far as SuperObject is concerned, sometimes it does make sense to parse unknown data and create its tree representation. I suppose that is what SuperObject does in your example. Whether or not it does full serialization to your class I have no idea.
Dalija Prasnikar I started the parser with any TPersistent descendants...but one reason I changed course is the ability to recognize/handle arrays of a given type. Given the limited RTTI of older versions of Delphi, the solution was to have a base type. This could be implemented quite differently in 2010+ versions with better RTTI...perhaps a second version could be made. I do not like the current implementation that comes with Delphi as it seems overly complex/fragile. They have a much wider audience to satisfy than I do. : )
ReplyDeleteDarian Miller We use arrays of objects before Delphi 2010, using a pre-definition of the array types of any type. No need of the extended RTTI. See http://synopse.info/files/html/api-1.18/mORMot.html#TJSONSERIALIZER_REGISTEROBJARRAYFORJSON
ReplyDeleteA. Bouchez Registering a class was a task I didn't want to require. I definitely gave it some thought, but (for me) the approach taken was the least effort/easiest to understand and something that I could easily slip-stream into the way I work. I don't know if you noticed, but in my code header in my JSONObjects unit is this:
ReplyDeleteThere will be the usual "But...X offers Y" comments. First response: if you want the best performance, use Synopse and not this class.
What happens when I want to use another framework that requires me to sub-class?
ReplyDeleteDavid Heffernan This would be many times easier to convert code to use another serialization format than using something like SuperObject for managing JSON, which this was specifically built in response to. If you want something that can handle any serialization output then there are other approaches. One would be to introduce a parent class to this, one would be to use the built-in serialization methods in the newer versions of Delphi...other solutions are out there. However, if you want to stop using horrid code like X.A['adresses'].O[1].S['adress'] := 'blabla', then I think this is a step up as you can use PODOs and lean on the compiler more.
ReplyDeleteDavid Heffernan I stumbled upon this yesterday that you could look into: JSON + XML + CSV serialization support: https://code.google.com/p/delphi-oop/
ReplyDelete+Darian You are presenting a false dichotomoty. There are not two ways to solve the problem. Subclassing imposes a gigantic constraint.
ReplyDeleteDavid Heffernan Not exactly sure how to respond well to that.. I gave you an alternative to try that offers multiple serialization options...and there's always more than one way to skin a cat. So I'll just say that this wasn't written to solve all of your issues, it was written to solve a specific one of mine. And it does that rather well for me.
ReplyDeleteDarian Miller We appreciate your desire to share your work and help others.
ReplyDeleteBut for reasons stated by David Heffernan , A. Bouchez and me, your library is of very limited use, not just for us, but to others as well.
In another words, we (I think I can speak for them too) are not looking for serialization frameworks ourselves, but we gave you some honest opinions and review of your work. Not because we want to tell you that your code is not good, but to teach you something. If you are willing to learn, of course.
That also does not mean that you have to jump and immediately change your code if it works well for you, just you should know that there is something wrong with it.
Dalija Prasnikar I appreciate that thought, but I didn't really write it to solve multiple problems as I don't really need it to at this time. I've been doing this for a very long time and I have rarely required an object to serialize into multiple output formats at the same time. (Typically if output formats change, the class also changes.) If I do, I'll keep your pointers in mind for the next iteration. I was assuming David was actually thinking about using it and had additional requirements (as I assumed he currently uses SuperObject based on StackOverflow posts), so I posted a possible alternative solution that he could check out which serializes objects to XML/JSON/CSV (which I believe happens to use SuperObject as part of the solution.) It's all good - just trying to help each other out.
ReplyDeleteDarian Miller I don't use SuperObject, or indeed any JSON in my Delphi code. The point is conceptual. Serialization should be possible without compelling the use of subclassing. For all the reasons mentioned by others.
ReplyDeleteDavid Heffernan Ok. I agree. But this is still pretty handy code which I'll use for now.
ReplyDeleteDarian Miller how to handle object with array of object in D5.
ReplyDeleteTItem = class(TdxJSONObject)
published
property code: String read Fcode write Fcode;
end
TMyArray = class(TdxJSONObject)
published
property codes: TdxJSONArrayOfObject read Fcodes write Fcodes;
end
Thanks