├── .abapgit.xml
├── CONTRIBUTING.md
├── CONTRIBUTING_USING_GENAI.md
├── LICENSE
├── LICENSES
└── Apache-2.0.txt
├── README.md
├── REUSE.toml
├── docs
├── advanced.md
├── basic.md
├── class-extension.md
├── data-access.md
├── faq.md
├── history.md
└── images
│ ├── generate_sbook1.png
│ ├── generate_sbook2.png
│ └── generate_sbook_optimized.png
└── src
├── package.devc.xml
├── z_ui2_data_access.clas.abap
├── z_ui2_data_access.clas.macros.abap
├── z_ui2_data_access.clas.testclasses.abap
├── z_ui2_data_access.clas.xml
├── z_ui2_json.clas.abap
├── z_ui2_json.clas.locals_imp.abap
├── z_ui2_json.clas.macros.abap
├── z_ui2_json.clas.testclasses.abap
└── z_ui2_json.clas.xml
/.abapgit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | E
6 | /src/
7 | PREFIX
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to an SAP Open Source Project
2 |
3 | ## General Remarks
4 |
5 | You are welcome to contribute content (code, documentation etc.) to this open source project.
6 |
7 | There are some important things to know:
8 |
9 | 1. You must **comply to the license of this project**, **accept the Developer Certificate of Origin** (see below) before being able to contribute. The acknowledgement to the DCO will usually be requested from you as part of your first pull request to this project.
10 | 2. Please **adhere to our [Code of Conduct](CODE_OF_CONDUCT.md)**.
11 | 3. If you plan to use **generative AI for your contribution**, please see our guideline below.
12 | 4. **Not all proposed contributions can be accepted**. Some features may fit another project better or doesn't fit the general direction of this project. Of course, this doesn't apply to most bug fixes, but a major feature implementation for instance needs to be discussed with one of the maintainers first. Possibly, one who touched the related code or module recently. The more effort you invest, the better you should clarify in advance whether the contribution will match the project's direction. The best way would be to just open an issue to discuss the feature you plan to implement (make it clear that you intend to contribute). We will then forward the proposal to the respective code owner. This avoids disappointment.
13 |
14 | ## Developer Certificate of Origin (DCO)
15 |
16 | Contributors will be asked to accept a DCO before they submit the first pull request to this projects, this happens in an automated fashion during the submission process. SAP uses [the standard DCO text of the Linux Foundation](https://developercertificate.org/).
17 |
18 | ## Contributing with AI-generated code
19 |
20 | As artificial intelligence evolves, AI-generated code is becoming valuable for many software projects, including open-source initiatives. While we recognize the potential benefits of incorporating AI-generated content into our open-source projects there a certain requirements that need to be reflected and adhered to when making contributions.
21 |
22 | Please see our [guideline for AI-generated code contributions to SAP Open Source Software Projects](CONTRIBUTING_USING_GENAI.md) for these requirements.
23 |
24 | ## How to Contribute
25 |
26 | 1. Make sure the change is welcome (see [General Remarks](#general-remarks)).
27 | 2. Create a branch by forking the repository and apply your change.
28 | 3. Commit and push your change on that branch.
29 | 4. Create a pull request in the repository using this branch.
30 | 5. Follow the link posted by the CLA assistant to your pull request and accept it, as described above.
31 | 6. Wait for our code review and approval, possibly enhancing your change on request.
32 | - Note that the maintainers have many duties. So, depending on the required effort for reviewing, testing, and clarification, this may take a while.
33 | 7. Once the change has been approved and merged, we will inform you in a comment.
34 | 8. Celebrate!
35 |
--------------------------------------------------------------------------------
/CONTRIBUTING_USING_GENAI.md:
--------------------------------------------------------------------------------
1 | # Guideline for AI-generated code contributions to SAP Open Source Software Projects
2 |
3 | As artificial intelligence evolves, AI-generated code is becoming valuable for many software projects, including open-source initiatives. While we recognize the potential benefits of incorporating AI-generated content into our open-source projects there a certain requirements that need to be reflected and adhered to when making contributions.
4 |
5 | When using AI-generated code contributions in OSS Projects, their usage needs to align with Open-Source Software values and legal requirements. We have established these essential guidelines to help contributors navigate the complexities of using AI tools while maintaining compliance with open-source licenses and the broader [Open-Source Definition](https://opensource.org/osd).
6 |
7 | AI-generated code or content can be contributed to SAP Open Source Software projects if the following conditions are met:
8 |
9 | 1. **Compliance with AI Tool Terms and Conditions**: Contributors must ensure that the AI tool's terms and conditions do not impose any restrictions on the tool's output that conflict with the project's open-source license or intellectual property policies. This includes ensuring that the AI-generated content adheres to the [Open-Source Definition](https://opensource.org/osd).
10 | 2. **Filtering Similar Suggestions**: Contributors must use features provided by AI tools to suppress responses that are similar to third-party materials or flag similarities. We only accept contributions from AI tools with such filtering options. If the AI tool flags any similarities, contributors must review and ensure compliance with the licensing terms of such materials before including them in the project.
11 | 3. **Management of Third-Party Materials**: If the AI tool's output includes pre-existing copyrighted materials, including open-source code authored or owned by third parties, contributors must verify that they have the necessary permissions from the original owners. This typically involves ensuring that there is an open-source license or public domain declaration that is compatible with the project's licensing policies. Contributors must also provide appropriate notice and attribution for these third-party materials, along with relevant information about the applicable license terms.
12 | 4. **Employer Policies Compliance**: If AI-generated content is contributed in the context of employment, contributors must also adhere to their employer’s policies. This ensures that all contributions are made with proper authorization and respect for relevant corporate guidelines.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/LICENSES/Apache-2.0.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
10 |
11 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
12 |
13 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
14 |
15 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
16 |
17 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
18 |
19 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
20 |
21 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
22 |
23 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
24 |
25 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
26 |
27 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
28 |
29 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
30 |
31 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
32 |
33 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
34 |
35 | (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
36 |
37 | (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
38 |
39 | (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40 |
41 | (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
42 |
43 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
44 |
45 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
46 |
47 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
48 |
49 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
50 |
51 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
52 |
53 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
54 |
55 | END OF TERMS AND CONDITIONS
56 |
57 | APPENDIX: How to apply the Apache License to your work.
58 |
59 | To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
60 |
61 | Copyright [yyyy] [name of copyright owner]
62 |
63 | Licensed under the Apache License, Version 2.0 (the "License");
64 | you may not use this file except in compliance with the License.
65 | You may obtain a copy of the License at
66 |
67 | http://www.apache.org/licenses/LICENSE-2.0
68 |
69 | Unless required by applicable law or agreed to in writing, software
70 | distributed under the License is distributed on an "AS IS" BASIS,
71 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
72 | See the License for the specific language governing permissions and
73 | limitations under the License.
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ABAP to JSON serializer and deserializer
2 |
3 | [](https://api.reuse.software/info/github.com/SAP/abap-to-json)
4 |
5 | ## About this project
6 |
7 | This is an Open-source version of the standard /UI2/CL_JSON class, and its public documentation was previously available on the SCN wiki as "One more ABAP to JSON Serializer and Deserializer" in 2013.
8 | The official delivery of /UI2/CL_JSON will continue. The Open Source version (Z_UI2_JSON) is your way to contribute and get updates sooner using ABAP Git infrastructure.
9 |
10 | ### Why
11 | There are a lot of other implementations of the **ABAP to JSON Serializer and Deserializer** in SDN. Still, I found all implementations unsuitable for my needs for different reasons. From SAP_BASIS 7.40 there is also **a simple transformation** available for converting **ABAP to JSON** and **JSON to ABAP**. It is the best choice if you need maximal performance and do not care about **serialization format**, but it fits badly for properly handling ABAP types and name **pretty-printing**.
12 |
13 | So, I have written my **ABAP JSON serializer** and **ABAP JSON deserializer**, which have some key differences from other implementations.
14 |
15 | Here, you can find an Open-Source version of the standard /UI2/CL_JSON class in the form of a Z* class that you can use as a local or global one.
16 |
17 | An original and current source version can be found in class /UI2/CL_JSON delivered with UI2 Add-on (can be applied to SAP_BASIS 740 – 76X). So, you can use this ABAP JSON parser in your standard code mostly on any system.
18 |
19 | ## Alternatives
20 | If for some reason the solution does not fit your purposes, there are other alternatives you may try:
21 | * [zJSON from Uwe Fetzer (aka se38)](https://github.com/se38/zJSON)
22 | * [aJSON from Alexander Tsybulsky (aka sbcgua)](https://github.com/sbcgua/ajson)
23 | * [ABAPify JSON ( ZCL_JSON )](https://github.com/abapify/json)
24 |
25 | ## Documentation
26 | * [Base Usage](docs/basic.md)
27 | * [Advanced Usage](docs/advanced.md)
28 | * [Class Extension](docs/class-extension.md)
29 | * [Dynamic Data Accessor](docs/data-access.md)
30 | * [FAQ](docs/faq.md)
31 | * [Version History](docs/history.md)
32 |
33 | ## The Code
34 | * [Code in abapGit format](src)
35 |
36 | ## Requirements and Setup
37 |
38 | Install via [abapGit Eclipse plugin](https://github.com/abapGit/ADT_Frontend) on ABAP cloud systems and [abapGit for SAPGUI](https://docs.abapgit.org/guide-online-install.html) on systems with SAP_BASIS 7.57 or higher.
39 |
40 | ## Support, Feedback, Contributing
41 |
42 | This project is open to feature requests/suggestions, bug reports, etc. via [GitHub issues](https://github.com/SAP/abap-to-json/issues). Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, and additional contribution information, see our [Contribution Guidelines](CONTRIBUTING.md). The contribution in the Open Source version will be then integrated into the standard SAP-delivered version (/UI2/CL_JSON).
43 | You can also use [Github Discussion](https://github.com/SAP/abap-to-json/discussions) to comment, request features or discuss the behavior.
44 |
45 | ## Code of Conduct
46 |
47 | Members, contributors, and leaders pledge to make participation in our community a harassment-free experience. By participating in this project, you agree to always abide by its [Code of Conduct](https://github.com/SAP/.github/blob/main/CODE_OF_CONDUCT.md).
48 |
49 | ## Licensing
50 |
51 | Copyright 2013-2023 SAP SE or an SAP affiliate company and contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/SAP/abap-to-json).
52 |
--------------------------------------------------------------------------------
/REUSE.toml:
--------------------------------------------------------------------------------
1 | version = 1
2 | SPDX-PackageName = "ABAP to JSON serializer and deserializer"
3 | SPDX-PackageSupplier = "ABAP 2 JSON Team "
4 | SPDX-PackageDownloadLocation = "https://github.com/SAP/abap-to-json"
5 | SPDX-PackageComment = "The code in this project may include calls to APIs (\"API Calls\") of\n SAP or third-party products or services developed outside of this project\n (\"External Products\").\n \"APIs\" means application programming interfaces, as well as their respective\n specifications and implementing code that allows the software to communicate with\n other software.\n API Calls to External Products are not licensed under the open-source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products, or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project's code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product or provides any third\n parties the right to use of access any SAP External Product, through API Calls."
6 |
7 | [[annotations]]
8 | path = "**"
9 | precedence = "aggregate"
10 | SPDX-FileCopyrightText = "2013-2023 SAP SE or an SAP affiliate company and abap-to-json contributors"
11 | SPDX-License-Identifier = "Apache-2.0"
12 |
--------------------------------------------------------------------------------
/docs/advanced.md:
--------------------------------------------------------------------------------
1 | # Overview
2 | - [CONSTRUCTOR and using instance methods](#constructor-and-using-instance-methods)
3 | - [Custom ABAP to JSON, JSON to ABAP name mapping](#custom-abap-to-json-json-to-abap-name-mapping)
4 | - [Custom formatting of values for serialization of ABAP into JSON](#custom-formatting-of-values-for-serialization-of-abap-into-json)
5 | - [Serialization/deserialization of hierarchical/recursive data](#serializationdeserialization-of-hierarchicalrecursive-data)
6 | - [Partial serialization/deserialization](#partial-serializationdeserialization)
7 | - [Deserialization of an untyped (unknown) JSON object](#deserialization-of-an-untyped-unknown-json-object)
8 | - [JSON/ABAP serialization/deserialization with runtime type information](#jsonabap-serializationdeserialization-with-runtime-type-information)
9 | - [Exception Handling in /UI2/CL_JSON](#exception-handling-in-ui2cl_json)
10 | - [JSON to ABAP transformation with the use of CALL TRANSFORMATION](#json-to-abap-transformation-with-the-use-of-call-transformation)
11 |
12 | # CONSTRUCTOR and using instance methods
13 | For simplicity, the class provides static SERIALIZE/DESERIALIZE methods that can be used without explicitly creating the class instance. However, the instance of the class is still created implicitly inside of the static method. If you have multiple usages of the class and would like to benefit from better performance and single initialization you may create the class instance manually, initialize the constructor once, and use the instance methods instead (SERIALIZE_INT/DESERIALIZE_INT/GENERATE_INT). Instance methods have fewer parameters than static ones, while the instance setting is provided in the constructor. In addition to performance gains, you also get more flexibility in class behavior customizing. The class CONSTRUCTOR has more flags as it is exposed in static methods.
14 |
15 | ## CONSTRUCTOR
16 |
17 | ### Common settings also exposed in dedicated static API
18 | * \> **COMPRESS** (bool, default = false) - Skip empty elements
19 | * \> **PRETTY_NAME** (PRETTY_NAME_MODE, default = PRETTY_MODE-NONE) - Pretty Print property names
20 | * \> **ASSOC_ARRAYS** (bool, default = false) - C_BOOL-FALSE Serialize tables with unique keys as associative array
21 | * \> **TS_AS_ISO8601** (bool, default = false) - C_BOOL-FALSE Dump timestamps as string in ISO8601 format
22 | * \> **EXPAND_INCLUDES** (bool, default = true) - Expand named includes in structures
23 | * \> **ASSOC_ARRAYS_OPT** (bool, default = false) - Optimize rendering of name-value maps
24 | * \> **NUMC_AS_STRING** (bool, default = false) - Serialize NUMC fields as strings
25 | * \> **NAME_MAPPINGS** (optional) - ABAP<->JSON Name Mapping Table
26 | * \> **CONVERSION_EXITS** (bool, default = false) - Use DDIC conversion exits on serialize/deserialize of values
27 | * \> **FORMAT_OUTPUT** (bool, default = false) - Indent and split in lines serialized JSON
28 | * \> **HEX_AS_BASE64** (bool, default = true) - Process hex values as base64
29 | * \> **GEN_OPTIMIZE** (bool, default = false) - Optimize generated structures for REF TO DATA
30 |
31 | ### Advanced settings available only with explicit constructor initialization
32 | * \> **STRICT_MODE** (bool, default = false) - Stop further processing on error
33 | * \> **BOOL_TYPES** (string, default = MC_BOOL_TYPES) - List of known boolean types
34 | * \> **BOOL_3STATE** (string, default = MC_BOOL_3STATE) - List of known 3state boolean types
35 | * \> **INITIAL_TS** (string, default = "") - Initial timestamp as JSON
36 | * \> **INITIAL_DATE** (string, default = "") - Initial date as JSON
37 | * \> **INITIAL_TIME** (string, default = "") Initial time as JSON
38 | * \> **TIME_ZONE Like** (string, default = UTC) - default time zone for conversion to date and time
39 |
40 | # Custom ABAP to JSON, JSON to ABAP name mapping
41 | By default, you control how JSON names are formatted/mapped to ABAP names by selecting proper pretty_mode as a parameter for the SERIALIZE/DESERIALIZE/GENERATE method. But sometimes, the standard, hard-coded formatting, is not enough. For example, if you need special rules for name formatting (for using special characters) or because the JSON attribute name is too long and can't be mapped to the ABAP name (which has a 30-character length limit).
42 |
43 | The recommended way for custom mapping was an extension of the class and redefining methods PRETTY_NAME or PRETTY_NAME_EX, but since note [2526405](https://launchpad.support.sap.com/#/notes/2526405) there is an easier way, without the need for its class. If you have a static list of field mappings from ABAP to JSON you can pass the name mapping table as a parameter for the constructor/serialize/deserialize and control how JSON names are formatted/mapped to ABAP names.
44 |
45 | ## ABAP to JSON name mapping example
46 | ```abap
47 | TYPES:
48 | BEGIN OF tp_s_data,
49 | sschema TYPE string,
50 | odatacontext TYPE string,
51 | shortened_abap_name TYPE string,
52 | standard TYPE string,
53 | END OF tp_s_data.
54 |
55 | DATA: ls_exp TYPE tp_s_data,
56 | lt_mapping TYPE /ui2/cl_json=>name_mappings,
57 | lv_json TYPE /ui2/cl_json=>json.
58 |
59 | lt_mapping = VALUE #( ( abap = `SSCHEMA` json = `$schema` )
60 | ( abap = `ODATACONTEXT` json = `@odata.context` )
61 | ( abap = `SHORTENED_ABAP_NAME` json = `VeeeeryyyyyLooooongJSONAttrbuuuuuuuuuteeeeeeeeeee` ) ).
62 |
63 | lv_json = /ui2/cl_json=>serialize( data = ls_exp name_mappings = lt_mapping ).
64 | ```
65 |
66 | # Custom formatting of values for serialization of ABAP into JSON
67 | Sometimes you need custom formatting for your ABAP data when serializing it into JSON. In another use case, you have some custom, DDIC-defined data types, that are not automatically recognized by standard code, so no appropriate formatting is applied (for example custom boolean or timestamp type).
68 |
69 | In such cases, you have the following options:
70 | 1. Extend the class and overwrite the method DUMP_TYPE. You can check an example in the section [class extension](class-extension.md).
71 | 2. Add conversion exits for your custom type and apply formatting as part of the conversion exit.
72 | 3. Create an alternative structure, with your custom types replaced by supported types, only for serialization, and do the move of data before the serialization.
73 |
74 | # Serialization/deserialization of hierarchical/recursive data
75 |
76 | Handling the recursive data structure in ABAP is not very trivial. And it is not very trivial to serialize and deserialize it either.
77 | If you would like to model your hierarchical data (tree-like) as ABAP structures, the only allowed way will be to do it like in the example below, where you use references to generic data:
78 | ```abap
79 | TYPES:
80 | BEGIN OF ts_node,
81 | id TYPE i,
82 | children TYPE STANDARD TABLE OF REF TO data WITH DEFAULT KEY,
83 | END OF ts_node.
84 |
85 | DATA: lv_exp TYPE string,
86 | lv_act TYPE string,
87 | ls_data TYPE ts_node,
88 | lr_data LIKE REF TO ls_data.
89 |
90 | ls_data-id = 1.
91 |
92 | CREATE DATA lr_data.
93 | lr_data->id = 2.
94 | APPEND lr_data TO ls_data-children.
95 | ```
96 | Such a way is more or less straightforward and will work, but it leads to losing type information for data stored in the children's table. That means you will need to cast data when you access it. In addition, it blocks you from deserializing such data from JSON, while the parser cannot deduce the type of data that needs to be created in the children's table. But serialization will work fine:
97 | ```abap
98 | lv_exp = '{"ID":1,"CHILDREN":[{"ID":2,"CHILDREN":[]}]}'.
99 | lv_act = /ui2/cl_json=>serialize( data = ls_data ).
100 | cl_aunit_assert=>assert_equals( act = lv_act exp = lv_exp msg = 'Serialization of recursive data structure fails' ).
101 | ```
102 | The better way to model hierarchical data in ABAP is with the help of objects. In contrast, objects are always processed as references and ABAP allows you to create nested data structures, referring to objects of the same type:
103 | ```abap
104 | CLASS lcl_test DEFINITION FINAL.
105 | PUBLIC SECTION.
106 | DATA: id TYPE i.
107 | DATA: children TYPE STANDARD TABLE OF REF TO lcl_test.
108 | ENDCLASS. "lcl_test DEFINITION
109 | ```
110 | In that manner, you can process data in the same way as with ABAP structures but using typed access and serialization/deserialization of data in JSON works fine while types can be deduced on
111 | ```abap
112 | DATA: lo_act TYPE REF TO lcl_test,
113 | lo_exp TYPE REF TO lcl_test,
114 | lv_json TYPE string,
115 | lo_child TYPE REF TO lcl_test.
116 |
117 | CREATE OBJECT lo_exp.
118 |
119 | lo_exp ->id = 1.
120 |
121 | CREATE OBJECT lo_child.
122 | lo_child->id = 2.
123 | APPEND lo_child TO lo_exp->children.
124 |
125 | lv_json = /ui2/cl_json=>serialize( data = lo_exp ).
126 | ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = lo_act ).
127 | ```
128 | Remark: There are some constraints for data design that exist regarding the deserialization of objects:
129 | * You cannot use constructors with obligatory parameters
130 | * References to interfaces will not be deserialized
131 |
132 | ## Serializing of protected and private attributes
133 | If you do the serialization from outside of your class, you can access only the public attributes of that class. To serialize all types of attributes (private+protected) you need to allow the JSON serializer class to access them. This can be done by defining the serializer class as a friend of your class. In this way, you do not disrupt your encapsulation for other classes but enable the serializer class to access all data of your class.
134 |
135 | If you do not own a class you want to serialize, you can inherit it from your class and add friends there. In this case, you can access at least protected attributes.
136 |
137 | # Partial serialization/deserialization
138 | When it is needed:
139 | * You deserialize JSON to ABAP but would like some known parts to be deserialized as JSON string, while you do not know the nesting JSON structure.
140 | * You deserialize a collection (array/associative array) having objects with heterogeneous structures (for example the same field has a different type depending on object type). Using partial deserialization, you can restore a type such as a JSON string in ABAP, and apply additional deserialization based on the object type later.
141 | * You serialize ABAP to JSON and have some ready JSON pieces (strings) you want to mix in.
142 |
143 | The solution /UI2/CL_JSON has for this type /UI2/CL_JSON=>JSON (alias for built-in type string). ABAP fields declared with this type will be serialized/deserialized as JSON pieces. During serialization from ABAP to JSON, the content of such JSON piece is not validated for correctness, so if you pass an invalid JSON block, it may destroy the complete resulting JSON string at the end.
144 |
145 | I've included examples of partial serialization/deserialization below.
146 |
147 | Serialization:
148 | ```abap
149 | TYPES: BEGIN OF ts_record,
150 | id TYPE string,
151 | columns TYPE /ui2/cl_json=>json,
152 | END OF ts_record.
153 |
154 | DATA: lv_json TYPE /ui2/cl_json=>json,
155 | lt_data TYPE SORTED TABLE OF ts_record WITH UNIQUE KEY id,
156 | ls_data LIKE LINE OF lt_data.
157 |
158 | ls_data-id = 'O000001ZZ_SO_GRES_CONTACTS'.
159 | ls_data-columns = '{"AGE":{"bVisible":true,"iPosition":2},"BRSCH":{"bVisible":true}}'.
160 | INSERT ls_data INTO TABLE lt_data.
161 |
162 | ls_data-id = 'O000001ZZ_TRANSIENT_TEST_A'.
163 | ls_data-columns = '{"ABTNR":{"bVisible":false},"CITY1":{"bVisible":false},"IC_COMPANY_KEY":{"bVisible":true}}'.
164 | INSERT ls_data INTO TABLE lt_data.
165 |
166 | lv_json = /ui2/cl_json=>serialize( data = lt_data assoc_arrays = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
167 |
168 | WRITE / lv_json.
169 | ```
170 | Results in:
171 | ```json
172 | {
173 | "O000001ZZ_SO_GRES_CONTACTS": {
174 | "columns": {
175 | "AGE": {
176 | "bVisible": true,
177 | "iPosition": 2
178 | },
179 | "BRSCH": {
180 | "bVisible": true
181 | }
182 | }
183 | },
184 | "O000001ZZ_TRANSIENT_TEST_A": {
185 | "columns": {
186 | "ABTNR": {
187 | "bVisible": false
188 | },
189 | "CITY1": {
190 | "bVisible": false
191 | },
192 | "IC_COMPANY_KEY": {
193 | "bVisible": true
194 | }
195 | }
196 | }
197 | }
198 | ```
199 | Deserialization:
200 | ```abap
201 | TYPES: BEGIN OF ts_record,
202 | id TYPE string,
203 | columns TYPE /ui2/cl_json=>json,
204 | END OF ts_record.
205 |
206 | DATA: lv_json TYPE string,
207 | lt_act TYPE SORTED TABLE OF ts_record WITH UNIQUE KEY id.
208 |
209 | CONCATENATE
210 | '{"O000001ZZ_SO_GRES_CONTACTS":{"columns":{"AGE":{"bVisible":true,"iPosition":2},"BRSCH":{"bVisible":true}}},'
211 | '"O000001ZZ_TRANSIENT_TEST_A":{"columns":{"ABTNR":{"bVisible":false},"CITY1":{"bVisible":false},"IC_COMPANY_KEY":{"bVisible":true}}}}'
212 | INTO lv_json.
213 |
214 | " If you know the first level of the underlying structure ("columns" field) -> Output Var 1
215 | /ui2/cl_json=>deserialize( EXPORTING json = lv_json assoc_arrays = abap_true CHANGING data = lt_act ).
216 |
217 | " if you do not know the underlying structure of the first level (naming of the second field e.g columns in the example does not matter )
218 | " => result is a little bit different -> Output Var 2
219 | /ui2/cl_json=>deserialize( EXPORTING json = lv_json assoc_arrays = abap_true assoc_arrays_opt = abap_true CHANGING data = lt_act ).
220 | ```
221 | Results in the following ABAP data:
222 | ## ABAP Output (variant 1)
223 | ```
224 | ID(CString) COLUMNS(CString)
225 | O000001ZZ_SO_GRES_CONTACTS {"AGE":{"bVisible":true,"iPosition":2},"BRSCH":{"bVisible":true}}
226 | O000001ZZ_TRANSIENT_TEST_A {"ABTNR":{"bVisible":false},"CITY1":{"bVisible":false},"IC_COMPANY_KEY":{"bVisible":true}}
227 | ```
228 | ## ABAP Output (variant 2)
229 | ```
230 | ID(CString) COLUMNS(CString)
231 | O000001ZZ_SO_GRES_CONTACTS {"columns":{"AGE":{"bVisible":true,"iPosition":2},"BRSCH":{"bVisible":true}}}
232 | O000001ZZ_TRANSIENT_TEST_A {"columns":{"ABTNR":{"bVisible":false},"CITY1":{"bVisible":false},"IC_COMPANY_KEY":{"bVisible":true}}}
233 | ```
234 |
235 | # Deserialization of an untyped (unknown) JSON object
236 | Suppose you need to deserialize a JSON object with an unknown structure, or you do not have a passing data type on the ABAP side, or the data type of the resulting object may vary. In that case, you can generate an ABAP object on the fly, using the corresponding GENERATE method. The method has some limitations compared to standard deserialization:
237 | * Fields are generated as a reference (even elementary types). If you want a more user-friendly generation - use the optimization flag (see below).
238 | * you can not control how deserialized arrays or timestamps
239 | * you can not access components of generated structure statically (while the structure is unknown at compile time) and need to use dynamic access
240 | * you need to accept default logic for type detection. Supported types are int, float, packaged, strings, boolean, date, time, and timestamps.
241 |
242 | The simplest example, with straightforward access:
243 | ```abap
244 | DATA: lv_json TYPE /ui2/cl_json=>json,
245 | lr_data TYPE REF TO data.
246 |
247 | FIELD-SYMBOLS:
248 | TYPE data,
249 | TYPE any,
250 | TYPE any.
251 |
252 | lv_json = `{"name":"Key1","properties":{"field1":"Value1","field2":"Value2"}}`.
253 | lr_data = /ui2/cl_json=>generate( json = lv_json ).
254 |
255 | " OK, generated, now let us access some field :(
256 | IF lr_data IS BOUND.
257 | ASSIGN lr_data->* TO .
258 | ASSIGN COMPONENT `PROPERTIES` OF STRUCTURE TO .
259 | IF IS ASSIGNED.
260 | lr_data = .
261 | ASSIGN lr_data->* TO .
262 | ASSIGN COMPONENT `FIELD1` OF STRUCTURE TO .
263 | IF IS ASSIGNED.
264 | lr_data = .
265 | ASSIGN lr_data->* TO .
266 | WRITE: . " We got it -> Value1
267 | ENDIF.
268 | ENDIF.
269 | ENDIF.
270 | ```
271 | A nice alternative, using [dynamic data accessor helper class](data-access.md):
272 | ```abap
273 | DATA: lv_json TYPE /ui2/cl_json=>json,
274 | lr_data TYPE REF TO data,
275 | lv_val TYPE string.
276 |
277 | lv_json = `{"name":"Key1","properties":{"field1":"Value1","field2":"Value2"}}`.
278 | lr_data = /ui2/cl_json=>generate( json = lv_json ).
279 |
280 | /ui2/cl_data_access=>create( ir_data = lr_data iv_component = `properties-field1`)->value( IMPORTING ev_data = lv_val ).
281 | WRITE: lv_val.
282 | ```
283 |
284 | ## Optimized type generation
285 | From PL19 it is possible to use the new switch for GENERATE (optimize) and DESERIALIZE (gen_optimize) to activate the optimization of generated ABAP data for REF TO DATA (fewer references, easily readable and accessible). But it results in longer processing for data generation (~25%).
286 |
287 | The generation without optimization flag:
288 |
289 | 
290 | 
291 |
292 | With optimization flag:
293 | 
294 |
295 | ## Implicit generation of ABAP objects on deserialization
296 |
297 | In addition to the explicit generation of the ABAP data objects from the JSON string, the deserializer supports an implicit way of generation, during DESERIALIZE(INT) call. To trigger generation, your output data structure shall contain a field with the type REF TO DATA, and the field name shall match the JSON attribute (pretty name rules are considered). Depending on the value of the field, the behavior may differ:
298 | * The value is not bound (initial): deserialize will use generation rules when creating corresponding data types of the referenced value
299 | * The value is bound (but may be empty): the deserializer will create a new referenced value based on the referenced type.
300 |
301 | ### Example of implicit generation of ABAP data from JSON string
302 | ```abap
303 | TYPES:
304 | BEGIN OF ts_dyn_data1,
305 | name TYPE string,
306 | value TYPE string,
307 | END OF ts_dyn_data1,
308 | BEGIN OF ts_dyn_data2,
309 | key TYPE string,
310 | value TYPE string,
311 | END OF ts_dyn_data2,
312 | BEGIN OF ts_data,
313 | str TYPE string,
314 | data TYPE REF TO data,
315 | END OF ts_data.
316 |
317 | DATA:
318 | ls_data TYPE ts_data,
319 | lv_json TYPE /ui2/cl_json=>json.
320 |
321 | lv_json = `{"str":"Test","data":{"name":"name1","value":"value1"}}`.
322 |
323 | " deserialize data and use generic generation for field "data",
324 | " the same as with method GENERATE (using temporary data type)
325 | /ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = ls_data ).
326 |
327 | " deserialize data and use type TS_DYN_DATA1 for the field "data"
328 | CREATE DATA ls_data-data TYPE ts_dyn_data1.
329 | /ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = ls_data ).
330 |
331 | " deserialize data and use alternative type TS_DYN_DATA2 for the field "data"
332 | CREATE DATA ls_data-data TYPE ts_dyn_data2.
333 | /ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = ls_data ).
334 | ```
335 |
336 | # JSON/ABAP serialization/deserialization with runtime type information
337 | Automatic deserialization of the JSON into the appropriate ABAP structure is not supported. The default implementation assumes that you need to know the target data structure (or at least partial structure, it will also work) to deserialize JSON in ABAP and then work with typed data.
338 |
339 | But if for some reason one needs the ability to deserialize JSON in source ABAP structure in a generic way, he can extend both serialize/deserialize methods and wrap outputs/inputs of /UI2/CL_JSON data by technical metadata describing source ABAP structure and use this information during deserialization (or use GENERATE method). Of course, you must ensure that the source ABAP data type is known in the deserialization scope (global and local types are "visible").
340 |
341 | See the example below:
342 | ```abap
343 | TYPES: BEGIN OF ts_json_meta,
344 | abap_type LIKE cl_abap_typedescr=>absolute_name,
345 | data TYPE string,
346 | END OF ts_json_meta.
347 |
348 | DATA: lt_flight TYPE STANDARD TABLE OF sflight,
349 | lv_json TYPE string,
350 | lo_data TYPE REF TO data,
351 | ls_json TYPE ts_json_meta.
352 |
353 | FIELD-SYMBOLS: TYPE any.
354 |
355 | SELECT * FROM sflight INTO TABLE lt_flight.
356 |
357 | * serialize table lt_flight into JSON, skipping initial fields and converting ABAP field names into camelCase
358 | ls_json-data = /ui2/cl_json=>serialize( data = lt_flight compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
359 | ls_json-abap_type = cl_abap_typedescr=>describe_by_data( lt_flight )->absolute_name.
360 | lv_json = /ui2/cl_json=>serialize( data = ls_json compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
361 | WRITE / lv_json.
362 |
363 | CLEAR: ls_json, lt_flight.
364 |
365 | * deserialize JSON string json into internal table lt_flight doing camelCase to ABAP like field name mapping
366 | /ui2/cl_json=>deserialize( EXPORTING json = lv_json pretty_name = /ui2/cl_json=>pretty_mode-camel_case CHANGING data = ls_json ).
367 | CREATE DATA lo_data TYPE (ls_json-abap_type).
368 | ASSIGN lo_data->* TO .
369 | /ui2/cl_json=>deserialize( EXPORTING json = ls_json-data pretty_name = /ui2/cl_json=>pretty_mode-camel_case CHANGING data = ).
370 |
371 | IF lo_data IS NOT INITIAL.
372 | BREAK-POINT. " check here lo_data
373 | ENDIF.
374 | ```
375 |
376 | # Exception Handling in /UI2/CL_JSON
377 | By default, /UI2/CL_JSON tries to hide from consumer-thrown exceptions (that may happen during deserialization) catching them at all levels. In some cases, it will result in missing attributes, in other cases, when an error is critical and the parser can not restore, you will get an empty object back. The main TRY/CATCH block prevents exceptions from the DESERIALIZE method.
378 |
379 | If you want to get a report in case of error, use the instance method DESERIALIZE_INT which may fire CX_SY_MOVE_CAST_ERROR. The reporting is rather limited - all errors are translated into CX_SY_MOVE_CAST_ERROR and no additional information is available. But from PL19 you will also get extra details reported in the target (ABAP) and source (JSON) fields, as the type of ABAP field, that is not filled and the JSON node leading to the error. More details can be found in [this issue](https://github.com/SAP/abap-to-json/pull/8).
380 |
381 | # JSON to ABAP transformation with the use of CALL TRANSFORMATION
382 | Below is a small example of CALL TRANSFORMATION usage to produce JSON from ABAP structures. Don't ask me for details - I do not know them. (smile) It was just a small test of me.
383 | ```abap
384 | DATA: lt_flight TYPE STANDARD TABLE OF sflight.
385 | SELECT * FROM sflight INTO TABLE lt_flight.
386 |
387 | * ABAP to JSON
388 | DATA(lo_writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
389 | CALL TRANSFORMATION id SOURCE text = lt_flight RESULT XML lo_writer.
390 | DATA(lv_jsonx) = lo_writer->get_output( ).
391 | DATA(lv_json) = /ui2/cl_json=>raw_to_string( lv_jsonx ).
392 |
393 | * JSON to ABAP
394 | CALL TRANSFORMATION id SOURCE XML lv_jsonx RESULT text = lt_flight.
395 | ```
396 | The transformation above uses the built-in ID transformation, that produces "ABAP JSON" output - that may be ok for you if you own the sender and receiver side and need the best performance. If you want your custom transformation rules you can use [this project](https://github.com/timostark/abap-json-serialization) to create an XSLT transformation for your data structure.
397 | If transformation for some reason does not work, please assist with the following notes: [1650141](http://service.sap.com/sap/support/notes/2368774) and [1648418](http://service.sap.com/sap/support/notes/2368774).
398 | See also the [blog of Horst Keller](http://scn.sap.com/community/abap/blog/2013/07/04/abap-news-for-release-740--abap-and-json) for more details.
399 |
400 | # Continue reading
401 | * [Basic usage of the class](basic.md)
402 | * [Extension (inheriting) of the class](class-extension.md)
403 | * [FAQ](faq.md)
404 | * [Version History](history.md)
405 |
--------------------------------------------------------------------------------
/docs/basic.md:
--------------------------------------------------------------------------------
1 | # Basic Usage
2 |
3 | # What the class can
4 | ## ABAP to JSON
5 | * Serialize classes, structures, internal tables, class and data references, and elementary types. Complex types, such as a table of structures/classes, classes with complex attributes, etc. are also supported and recursively processed.
6 | * **ABAP to JavaScript** adopted way of data type serializations:
7 | * strings, character types to JavaScript string format (no length limitation),
8 | * ABAP_BOOL / BOOLEAN / XFELD / BOOLE_D to JavaScript Boolean,
9 | * Built-in TRIBOOL (TRUE/FALSE/UNDEFINED = 'X'/'-'/'') support, for better control of initial values when serializing into JavaScript Boolean
10 | * int/floats/numeric/packed to JavaScript Integers/floats,
11 | * date/time to JavaScript date/time string representation as "2015-03-24" or "15:30:48",
12 | * timestamp to JavaScript integer or ISO8601 string
13 | * structures to JavaScript objects (include types are also supported; aliases => AS are ignored)
14 | * **convert ABAP internal table to JSON**, e.g JavaScript arrays or associative arrays (objects)
15 | * Support of conversion exits on ABAP data serialization
16 | * Pretty Printing of JavaScript property names: MY_DATA -> myData, /SAPAPO/MY_DATA -> sapapoMyData.
17 | * Condensing of default values: initial values are not rendered into the resulting JSON string
18 | * Optionally apply **JSON formatting/beautifing/pretty-print** for serialized JSON
19 | * Performance is optimized for processing big internal tables with structures
20 |
21 | ## JSON to ABAP
22 | * Deserialize JSON objects, arrays, and elementary types into corresponding ABAP structures. Complex objects, with embedded arrays and objects with any level of nesting, are also supported.
23 | * **Convert JSON to an internal table**
24 | * Generic deserialization of JSON objects into reference data types:
25 | * as simple data types (integer, boolean, or string into generic data reference (REF TO DATA) -> ABAP type is selected based on JSON type.
26 | * as dynamically generated complex object (structures, tables, mixed) for initial REF TO DATA fields
27 | * as typed references for prefilled REF TO DATA fields (you assign a reference to a typed empty data object to the REF TO DATA field in execution time)
28 | * Deserialization of unknown JSON structures possible using method GENERATE into on-the-fly created data types
29 | * On **JSON** to **ABAP** transformation following rules are used:
30 | * objects parsed into corresponding ABAP structures, classes (only classes with constructors with no obligatory parameters are supported), or internal hash/sorted tables
31 | * arrays converted to internal tables (complex tables are also supported).
32 | * Boolean converted as ABAP_BOOL (‘’ or ‘X’)
33 | * Date/Time/Timestamps from JSON converted based on the type of corresponding ABAP element
34 | * integers/floats/strings moved to corresponding fields using ABAP move semantic (strings are un-escaped). There is no limit on the size of deserialized strings, the only restriction is the constraints of receiving data type. Escaped Unicode symbols (\u001F) in strings are decoded.
35 | * elementary data types are converted if they do not match: JavaScript integer can come into ABAP string or JavaScript string into ABAP integer, etc.
36 | * Transformation considers property naming guidelines for **JSON** and **ABAP** so that camelCase names will be copied into the corresponding CAMEL_CASE field if the CAMELCASE field is not found in the ABAP structure. Please don't forget to use the same PRETTY_MODE for deserialization, as you have used for serialization.
37 | * Default field values, specified in reference ABAP variable are preserved, and not overwritten if not found in the JSON object
38 | * Transformation of **JSON** structures into ABAP class instances is NOT supported.
39 | * Support of conversion exits on deserialization
40 |
41 | The parser for serializing/deserializing uses single-pass parsing and is optimized to provide the best possible performance in ABAP in a release-independent way. But for time-critical applications, which have kernel version 7.20 and higher, it is recommended to use built-in [**JSON** to **ABAP** transformations (CALL TRANSFORMATION)](advanced.md#json-to-abap-transformation-with-the-use-of-call-transformation).
42 |
43 | # Usage example
44 | ## ABAP to JSON usage example
45 | ```abap
46 | CLASS demo DEFINITION.
47 | PUBLIC SECTION.
48 | CLASS-METHODS main.
49 | ENDCLASS.
50 |
51 | CLASS demo IMPLEMENTATION.
52 | METHOD main.
53 |
54 | DATA: lt_flight TYPE STANDARD TABLE OF sflight,
55 | lrf_descr TYPE REF TO cl_abap_typedescr,
56 | lv_json TYPE /ui2/cl_json=>json.
57 |
58 |
59 | SELECT * FROM sflight INTO TABLE lt_flight.
60 |
61 | " serialize table lt_flight into JSON, skipping initial fields and converting ABAP field names into camelCase
62 | lv_json = /ui2/cl_json=>serialize( data = lt_flight
63 | pretty_name = /ui2/cl_json=>pretty_mode-camel_case
64 | compress = abap_true
65 | ).
66 |
67 | cl_demo_output=>write_json( lv_json ).
68 |
69 | CLEAR lt_flight.
70 |
71 | " deserialize JSON string json into internal table lt_flight doing camelCase to ABAP like field name mapping
72 | /ui2/cl_json=>deserialize( EXPORTING json = lv_json pretty_name = /ui2/cl_json=>pretty_mode-camel_case
73 | CHANGING data = lt_flight ).
74 |
75 | " serialize ABAP object into JSON string
76 | lrf_descr = cl_abap_typedescr=>describe_by_data( lt_flight ).
77 | lv_json = /ui2/cl_json=>serialize( data = lrf_descr format_output = abap_true ).
78 |
79 | cl_demo_output=>write_json( lv_json ).
80 |
81 | cl_demo_output=>display( ).
82 |
83 | ENDMETHOD.
84 | ENDCLASS.
85 |
86 | START-OF-SELECTION.
87 | demo=>main( ).
88 | ```
89 |
90 | ## JSON Output 1
91 | ```json
92 | [
93 | {
94 | "mandt": "120",
95 | "carrid":"AA",
96 | "connid":17,
97 | "fldate":"2018-08-15",
98 | "price":422.94,
99 | "currency":"USD",
100 | "planetype":"747-400",
101 | "seatsmax":385,
102 | "seatsocc":268,
103 | "paymentsum":192361.84
104 | },
105 | {
106 | "mandt":"120",
107 | "carrid":"AA",
108 | "connid":17
109 | },
110 | ...
111 | ```
112 | ```json
113 | {
114 | "ABSOLUTE_NAME":"\\TYPE=%_T00004S00000000O0000014656",
115 | "DECIMALS":0,
116 | "HAS_UNIQUE_KEY":false,
117 | "INITIAL_SIZE":0,
118 | "KEY":
119 | [
120 | {
121 | "NAME":"MANDT"
122 | },
123 | {
124 | "NAME":"CARRID"
125 | },
126 | {
127 | "NAME":"CONNID"
128 | }
129 | ....
130 | ```
131 | # API description
132 | Two static methods are most interesting in common cases: SERIALIZE and DESERIALIZE. The rest of the public methods are defined as public only for reuse purposes if you want to build/extend your own serialization/deserialization code.
133 |
134 | ## SERIALIZE: Serialize ABAP object into JSON
135 |
136 | * \> **DATA** (any) - any ABAP object/structure/table/element to be serialized
137 | * \> **COMPRESS** (bool, default = false) - tells serializer to skip empty elements/objects during serialization. So, all of which IS INITIAL = TRUE.
138 | * \> **NAME** (string, optional) - optional name of the serialized object. Will '"name" : {...}' instead of ' {...} ' if supplied.
139 | * \> **PRETTY_NAME** (enum, optional)- mode controls how ABAP field names are transformed into JSON attribute names. More can be found in the description below.
140 | * \> **TYPE_DESCR** (ref to CL_ABAP_TYPEDESCR, optional) - if you already know the object type, pass it to improve performance.
141 | * \> **ASSOC_ARRAYS** (bool, default = false) - controls how to serialize hash or sorted tables with unique keys. More can be found in the description below.
142 | * \> **ASSOC_ARRAYS_OPT** (bool, default = false) - when set, the serializer will optimize the rendering of name-value associated arrays (hash maps) in JSON
143 | * \> **TS_AS_ISO8601** (bool, default = false) - says serializer to output timestamps using ISO8601 format.
144 | * \> **NUMC_AS_STRING** (bool, default = false) - Controls how NUMC fields are serialized. If set to ABAP_TRUE, NUMC fields are serialized not as integers, but as strings, with all leading zeros. Deserialization works compatible with both ways of NUMC serialized data.
145 | * \> **NAME_MAPPINGS** (table) - ABAP<->JSON Name Mapping Table
146 | * \> **CONVERSION_EXITS** (bool, default = false) - use DDIC conversion exits on serialize of values (performance loss!)
147 | * \> **FORMAT_OUTPUT** (bool, default = false) - Indent, add formatting spaces, and split into lines serialized JSON
148 | * \> **HEX_AS_BASE64** (bool, default = true) - Serialize hex values as base64
149 | * \< **R_JSON** - output JSON string.
150 |
151 | ## DESERIALIZE: Deserialize ABAP object from JSON string
152 |
153 | * \> **JSON** (string) - input JSON object string to deserialize
154 | * \> **JSONX** (xstring) - input JSON object as a raw string to deserialize
155 | * \> **PRETTY_NAME** (enum, optional) - mode, controlling how JSON field names are mapped to ABAP component names. More can be found in the description below.
156 | * \> **ASSOC_ARRAYS** (bool, default = false) - controls how to deserialize JSON objects into hash or sorted tables with unique keys. More can be found in the description below.
157 | * \> **ASSOC_ARRAYS_OPT** (bool, default = false) - when set, the deserializer will take into account the optimized rendering of associated arrays (properties) in JSON.
158 | * \> **NAME_MAPPINGS** (table) - ABAP<->JSON Name Mapping Table
159 | * \> **CONVERSION_EXITS** (bool, default = false) - use DDIC conversion exits on deserialize of values (performance loss!)
160 | * \> **HEX_AS_BASE64** (bool, default = true) - deserialize hex values as base64
161 | * \> **[GEN_OPTIMIZE](advanced.md#deserialization-of-an-untyped-unknown-json-object)** (bool, default = false) - optimize generated types, structures, and table types for REF TO DATA
162 | * \<\> **DATA** (any) - ABAP object/structure/table/element to be filled from JSON string. If the ABAP structure contains more fields than in the JSON object, the content of unmatched fields is preserved.
163 |
164 | ## GENERATE: Generates ABAP object from JSON
165 |
166 | * \> **JSON** (string) - input JSON object string to generate
167 | * \> **JSONX** (xstring) - input JSON object as a raw string to generate
168 | * \> **PRETTY_NAME** (enum, optional) - mode, controlling how JSON field names are mapped to ABAP component names. More can be found in the description below.
169 | * \> **[OPTIMIZE](advanced.md#deserialization-of-an-untyped-unknown-json-object)** (bool, default = false) - optimize generated types, structures, and table types for REF TO DATA
170 | * \> **NAME_MAPPINGS** (table) - ABAP<->JSON Name Mapping Table
171 | * \< **RR_DATA** (REF TO DATA) - a reference to ABAP structure/table dynamically generated from JSON string.
172 |
173 | In addition to the explained methods, there are two options, that need a wider explanation:
174 |
175 | ## PRETTY_NAME : enumeration of modes, defined as constant /UI2/CL_JSON=>pretty_name.
176 |
177 | * **NONE** - ABAP component names are serialized as is (UPPERCASE).
178 | * **LOW_CASE** - ABAP component names serialized in low case
179 | * **CAMEL_CASE** - ABAP component types serialized in CamelCase where symbol "\_" is treated as a word separator (and removed from the resulting name).
180 | * **EXTENDED** - works the same way as CAMEL_CASE but also, has extended logic for encoding special characters, such as: ".", "@", "~", etc. It shall be used if you need JSON names with characters not allowed for ABAP data component names. If you do not have special characters in JSON names, do not use it - the performance will be slower than CAMEL_CASE mode. Example: ABAP name '\_\_A\_\_SCHEMA' translates in JSON name '@schema'
181 | Encoding rules (ABAP name → JSON name):
182 | * '\_\_E\_\_' → '!'
183 | * '\_\_N\_\_' → '#'
184 | * '\_\_D\_\_' → '$'
185 | * '\_\_P\_\_' → '%'
186 | * '\_\_M\_\_' → '&'
187 | * '\_\_S\_\_' → '*'
188 | * '\_\_H\_\_' → '-'
189 | * '\_\_T\_\_' → '~'
190 | * '\_\_L\_\_' → '/'
191 | * '\_\_C\_\_' → ':'
192 | * '\_\_V\_\_' → '|'
193 | * '\_\_A\_\_' → '@'
194 | * '\_\_O\_\_' or '\_\_\_' → '.'
195 | **NONE** and **LOW_CASE** work the same way for DESERIALIZE.
196 |
197 | ## ASSOC_ARRAYS :
198 |
199 | This option controls how hashed or sorted tables with unique keys are serialized/deserialized. Normally, ABAP internal tables are serialized into JSON arrays. Still, in some cases, you will like to serialize them as associative arrays (JSON objects) where every table row shall be reflected as a separate property of the JSON object. This can be achieved by setting the *ASSOC_ARRAYS* parameter to TRUE. If set, the serializer checks for sorted/hashed tables with a UNIQUE key(s) and serialize them as an object. The JSON property name, reflecting row, constructed from values of fields, used in key separated by constant *MC_KEY_SEPARATOR* = '-'. If the table has only one field marked as key, the value of this single field becomes a property name and is REMOVED from the associated object (to eliminate redundancy). If TABLE_LINE is used as a unique key, all values of all fields construct key property names (separated by *MC_KEY_SEPARATOR*). During deserialization, logic works vice versa: if *ASSOC_ARRAYS* is set to TRUE, and the JSON object matches the internal hash or sorted table with the unique key, the object is transformed into the table, where every object property is reflected in a separated table row. If the ABAP table has only one key field, the property name is transformed into a value of this key field.
200 |
201 | ## ASSOC_ARRAYS_OPT:
202 |
203 | By default, when dumping hash/sorted tables with a unique key into JSON, the serializer will write the key field as the property name, and the rest of the fields will write the object value of properties:
204 | ```abap
205 | TYPES: BEGIN OF ts_record,
206 | key TYPE string,
207 | value TYPE string,
208 | END OF ts_record.
209 |
210 | DATA: lt_act TYPE SORTED TABLE OF ts_record WITH UNIQUE KEY key.
211 | lv_json = /ui2/cl_json=>serialize( data = lt_exp assoc_arrays = abap_true ).
212 | ```
213 | Output JSON
214 | ```json
215 | {
216 | "KEY1": {
217 | "value": "VALUE1"
218 | },
219 | "KEY2": {
220 | "value": "VALUE2"
221 | }
222 | }
223 | ```
224 | But suppose you will use the assoc_arrays_opt flag during serialization. In that case, the serializer will try to omit unnecessary object nesting by dumping simple, name/value tables, containing only one key field and one value field:
225 | ```abap
226 | lv_json = /ui2/cl_json=>serialize( data = lt_exp assoc_arrays = abap_true assoc_arrays_opt = abap_true ).
227 | ```
228 | Output JSON
229 | ```json
230 | {
231 | "KEY1": "VALUE1",
232 | "KEY2": "VALUE2"
233 | }
234 | ```
235 | For deserialization, the flag tells the deserializer that the value shall be placed in a non-key field of the structure.
236 |
237 |
238 | Here is an example of deserialization, in which mentioned above two options can be used:
239 | ```json
240 | {
241 | "id": "509e28db-6794-4e02-91d3-e35f4c7310e9",
242 | "form": {
243 | "Authorised": "TBB ENTERPRISE SDN. BHD.",
244 | "Fax No:": "92225491",
245 | "Issued Date :": "02-10-2024",
246 | "Voluntary Excess": "0.00",
247 | "Page": "2",
248 | "Allianz General Insurance Company (Malaysia) Berhad": "(200601015674)",
249 | }
250 | }
251 | ```
252 | Look into sub-attributes of the "form" field. They can not be easily mapped into any ABAP structure, because attribute names contain spaces, special characters, etc. The best way would be to deserialize this fragment in a table with a key, corresponding to the attribute name. The example code for deserialization then will look like this:
253 | ```abap
254 | TYPES: BEGIN OF ts_name_value,
255 | name TYPE string,
256 | value TYPE string,
257 | END OF ts_name_value,
258 | BEGIN OF ts_data,
259 | id TYPE string,
260 | form TYPE SORTED TABLE OF ts_name_value WITH UNIQUE KEY name,
261 | END OF ts_data.
262 |
263 | DATA: ls_data TYPE ts_data.
264 | /ui2/cl_json=>deserialize( EXPORTING json = lv_json
265 | assoc_array = abap_true
266 | assoc_array_opt = abap_true
267 | CHANGING data = ls_data ).
268 | ```
269 |
270 | # Supported SAP_BASIS releases
271 | The code was tested from SAP_BASIS 7.00 and higher, but I do not see the reasons why it cannot be downported on lower releases either. But if you plan to use it on SAP_BASIS 7.02 and higher (and do not need property name pretty-printing) better consider the standard solution for **ABAP**, using [CALL TRANSFORMATION](advanced.md#json-to-abap-transformation-with-the-use-of-call-transformation). It shall be faster, while implemented in the kernel. Maybe the best will be, if you need support in lower SAP_BASIS releases as well as in 7.02 and higher, to modify the provided class in a way to generate the same **JSON** format as standard ABAP CALL TRANSFORMATION for JSON does and redirect flow to home-made code or built-in **ABAP** transformation depending on SAP_BASIS release.
272 |
273 | # Further optimizations
274 | * Be aware, that usage of flag conversion_exits may significantly decrease performance - use only in cases, when you are sure that you need it.
275 | * Escaping property values can be expensive. To optimize performance, in this case, you can replace escapement code with some kernel-implemented function (from cl_http_utility class for example), instead of explicit *REPLACE ALL OCCURRENCES* calls.
276 | * Unescaping can influence deserialization performance even worse, depending on the fact that your JSON has encoded \n\r\t\f\b\x. So, avoid their usage if you can.
277 | * It is possible to significantly increase performance for serialization/deserialization by dropping the support of releases below 7.40. That can be realized by moving base parsing from ABAP to kernel-implemented classes cl_sxml_string_writer and cl_sxml_string_reader.
278 |
279 | # Remarks
280 | Due to optimization reasons, some methods were converted to macros, to reduce overhead for calling methods for data type serialization. If performance in your case is not critical, and you prefer clean/debuggable code you can replace macro calls with corresponding methods.
281 |
282 | # Continue reading
283 | * [Advanced Use cases](advanced.md)
284 | * [Extension (inheriting) of the class](class-extension.md)
285 | * [FAQ](faq.md)
286 | * [Version History](history.md)
287 |
288 | # Related pages
289 | * [Does /UI2/CL_JSON work in ABAP Cloud?](https://answers.sap.com/questions/12699592/does-ui2cl-json-work-in-abap-cloud.html)
290 | * [Return generic result via SAP RFC (performance)](https://stackoverflow.com/questions/54037544/abap-return-generic-result-via-sap-rfc-json)
291 | * [RFC7159 - The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159)
292 | * [OData JSON Deserialize](https://answers.sap.com/questions/12998367/odata-simple-json-deserialize.html?childToView=13007205)
293 |
--------------------------------------------------------------------------------
/docs/class-extension.md:
--------------------------------------------------------------------------------
1 | # /UI2/CL_JSON extension
2 |
3 | If standard class functionality does not fit your requirements there are two ways how you can adapt it to your needs:
4 |
5 | * Use a local copy of the class /UI2/CL_JSON and modify logic directly by changing the original code.
6 | * Inherit from class /UI2/CL_JSON and override methods where another logic is required.
7 |
8 | The advantage of the first approach is that you are completely free in what you may change and have full control of the class lifecycle. The disadvantage is that you must merge your changes with /UI2/CL_JSON updates.
9 |
10 | For the second approach, you can use /UI2/CL_JSON directly (the prerequisite is the latest version of note [2330592](https://github.com/SAP/abap-to-json/blob/main/docs/history.md#note-2330592)), you do not need to care about the merge but can override only some methods. The methods are:
11 |
12 | ## IS_COMPRESSIBLE
13 | IS_COMPRESSIBLE is called to check, if the given type output may be suppressed during ABAP to JSON serialization when a value is initial.
14 |
15 | * \> **TYPE_DESCR** (ref to CL_ABAP_TYPEDESCR) – value type
16 | * \< **RV_COMPRESS** (bool) – compress initial value
17 | The default implementation of the method allows compressing any initial value.
18 |
19 | ## PRETTY_NAME
20 | PRETTY_NAME – called to format ABAP field name written to **JSON** or deserialized from **JSON** to **ABAP** field, when the pretty_name parameter of SERIALIZE/DESERIALIZE method equal to PRETTY_MODE-CAMEL_CASE.
21 |
22 | * \> **IN** (CSEQUENCE) – Field name to pretty print.
23 | * \< **OUT** (STRING) – Pretty printed field name
24 | The default implementation applies camelCase formatting using the “\_” symbol. To output, the “\_” symbol, use the double “\_\_” symbol in the field name.
25 |
26 | ## PRETTY_NAME_EX
27 | PRETTY_NAME_EX – called to format ABAP field name written to JSON or deserialized from JSON to ABAP field, when the pretty_name parameter of *SERIALIZE/DESERIALIZE* method equal to *PRETTY_MODE-EXTENDED*.
28 |
29 | * \> **IN** (CSEQUENCE) – Field name to pretty print.
30 | * \< **OUT** (STRING) – Pretty printed field name
31 | The default implementation does the same as PRETTY_NAME, plus converting special characters "!#$%&\*-~/:|@.".
32 |
33 | ## DUMP_INT
34 | DUMP_INT - called for recursive serialization of complex ABAP data objects (structure, class, table) into JSON string
35 |
36 | * \> **DATA** (DATA) – Any data to serialize.
37 | * \> **TYPE_DESCR** (ref to CL_ABAP_TYPEDESCR, optional) – Type of data provided
38 | * \< **R_JSON** (JSON) – serialized JSON value
39 |
40 | ## DUMP_TYPE
41 | DUMP_TYPE - called for serialization of elementary ABAP data type (string, boolean, timestamp, etc) into the JSON attribute value. Overwrite it if you, for example, want to apply data output data conversion of currency rounding
42 | * \> **DATA** (DATA) – Any data to serialize
43 | * \> **TYPE_DESCR** (ref to CL_ABAP_TYPEDESCR) – Type of data provided
44 | * \< **R_JSON** (JSON) – serialized JSON value
45 |
46 | ## RESTORE
47 | RESTORE - called for deserializing JSON objects into ABAP structures
48 |
49 | * \> **JSON** (JSON) – JSON string to deserialize
50 | * \> **LENGTH** (I) – Length of the JSON string
51 | * \> **TYPE_DESCR** (ref to CL_ABAP_TYPEDESCR, optional) – Type of changing data provided
52 | * \> **FIELD_CACHE** (type T_T_FIELD_CACHE, optional) – Cache of ABAP data fields with type information
53 | * \<\> **DATA** (type DATA, optional) – ABAP data object to fill
54 | * \<\> **OFFSET** (I) – parsing start point in JSON string
55 |
56 | ## RESTORE_TYPE
57 | RESTORE_TYPE - called to deserialize simple JSON attributes and JSON arrays
58 |
59 | * \> **JSON** (JSON) – JSON string to deserialize
60 | * \> **LENGTH** (I) – Length of the JSON string
61 | * \> **TYPE_DESCR** (ref to CL_ABAP_TYPEDESCR, optional) – Type of changing data provided
62 | * \> **FIELD_CACHE** (type T_T_FIELD_CACHE, optional) – Cache of ABAP data fields with type information
63 | * \<\> **DATA** (type DATA, optional) – ABAP data object to fill
64 | * \<\> **OFFSET** (I) – parsing start point in JSON string
65 |
66 | ## CLASS_CONSTRUCTOR
67 | CLASS_CONSTRUCTOR - used to initialize static variables. You can not overwrite it but implement your class constructor that adapts default globals. For example, add boolean types to be recognized during serialization/deserialization.
68 |
69 | ## SERIALIZE/DESERIALIZE
70 | SERIALIZE/DESERIALIZE - these methods are static and cannot be redefined. Methods are helpers for a consumption code, hiding the construction of the class instance and further \*\_INT calls. So, if you would like to use something similar, in your custom class, you need to copy the mentioned methods to new ones e,g \*\_EX and overwrite there /UI2/CL_JSON type to your custom class name. And use these methods instead of standard.
71 |
72 | Extension using inheritance:
73 | ```abap
74 | CLASS lc_json_custom DEFINITION FINAL INHERITING FROM /ui2/cl_json.
75 | PUBLIC SECTION.
76 | CLASS-METHODS:
77 | class_constructor,
78 | deserialize_ex IMPORTING json TYPE json OPTIONAL
79 | pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none
80 | CHANGING data TYPE data,
81 | serialize_ex IMPORTING data TYPE data
82 | compress TYPE bool DEFAULT c_bool-false
83 | pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none
84 | RETURNING value(r_json) TYPE json.
85 |
86 | PROTECTED SECTION.
87 | METHODS:
88 | is_compressable REDEFINITION,
89 | pretty_name REDEFINITION,
90 | dump_type REDEFINITION.
91 | ENDCLASS. "lc_json_custom DEFINITION
92 |
93 | CLASS lc_json_custom IMPLEMENTATION.
94 |
95 | METHOD class_constructor.
96 | CONCATENATE mc_bool_types `\TYPE=/UI2/BOOLEAN` INTO mc_bool_types.
97 | ENDMETHOD. "class_constructor
98 |
99 | METHOD is_compressable.
100 | IF type_descr->absolute_name EQ `\TYPE=STRING` OR name EQ `INITIAL`.
101 | rv_compress = abap_false.
102 | ELSE.
103 | rv_compress = abap_true.
104 | ENDIF.
105 | ENDMETHOD. "is_compressable
106 |
107 | METHOD pretty_name.
108 | out = super->pretty_name( in ).
109 | CONCATENATE out 'Xxx' INTO out.
110 | ENDMETHOD. "pretty_name
111 |
112 | METHOD dump_type.
113 |
114 | DATA: is_ddic TYPE abap_bool,
115 | ddic_field TYPE dfies.
116 |
117 | is_ddic = type_descr->is_ddic_type( ).
118 | IF is_ddic EQ abap_true.
119 | ddic_field = type_descr->get_ddic_field( ).
120 | IF mv_ts_as_iso8601 EQ c_bool-true AND ddic_field-domname EQ `TZNTSTMPL`.
121 | r_json = data.
122 | CONCATENATE `"` r_json(4) `-` r_json+4(2) `-` r_json+6(2) `T` r_json+8(2) `:` r_json+10(2) `:` r_json+12(2) `.` r_json+15(7) `Z"` INTO r_json.
123 | RETURN.
124 | ENDIF.
125 | ENDIF.
126 | IF mv_ts_as_iso8601 EQ c_bool-true AND type_descr->absolute_name EQ `\TYPE=LCM_CHANGED_ON`.
127 | r_json = data.
128 | CONCATENATE `"` r_json(4) `-` r_json+4(2) `-` r_json+6(2) `T` r_json+8(2) `:` r_json+10(2) `:` r_json+12(2) `.` r_json+15(7) `Z"` INTO r_json.
129 | RETURN.
130 | ENDIF.
131 |
132 | r_json = super->dump_type( data = data type_descr = type_descr convexit = convexit ).
133 |
134 | ENDMETHOD. "dump_type
135 |
136 | METHOD serialize_ex.
137 | DATA: lo_json TYPE REF TO lc_json_custom.
138 | CREATE OBJECT lo_json
139 | EXPORTING
140 | compress = compress
141 | pretty_name = pretty_name
142 | assoc_arrays = abap_true
143 | assoc_arrays_opt = abap_true
144 | expand_includes = abap_true
145 | numc_as_string = abap_true
146 | ts_as_iso8601 = abap_true.
147 | r_json = lo_json->serialize_int( data = data ).
148 | ENDMETHOD. "serialize_ex
149 |
150 | METHOD deserialize_ex.
151 | DATA: lo_json TYPE REF TO lc_json_custom.
152 | IF json IS NOT INITIAL.
153 | CREATE OBJECT lo_json
154 | EXPORTING
155 | pretty_name = pretty_name
156 | assoc_arrays = abap_true
157 | assoc_arrays_opt = abap_true.
158 | TRY .
159 | lo_json->deserialize_int( EXPORTING json = json CHANGING data = data ).
160 | CATCH cx_sy_move_cast_error.
161 | ENDTRY.
162 | ENDIF.
163 | ENDMETHOD. "deserialize_ex
164 |
165 | ENDCLASS. "lc_json_custom IMPLEMENTATION
166 |
167 | TYPES:
168 | BEGIN OF tp_s_data,
169 | tribool TYPE lc_json_custom=>tribool,
170 | bool TYPE lc_json_custom=>bool,
171 | str1 TYPE string,
172 | str2 TYPE string,
173 | initial TYPE i,
174 | END OF tp_s_data.
175 |
176 | DATA: ls_exp TYPE tp_s_data,
177 | ls_act LIKE ls_exp,
178 | lo_json_custom TYPE REF TO lc_json_custom,
179 | lv_json_custom TYPE lc_json_custom=>json.
180 |
181 | ls_exp-tribool = lc_json_custom=>c_tribool-false.
182 | ls_exp-bool = lc_json_custom=>c_bool-false.
183 | ls_exp-str1 = ''.
184 | ls_exp-str2 = 'ABC'.
185 | ls_exp-initial = 0.
186 | CREATE OBJECT lo_json_custom
187 | EXPORTING
188 | compress = abap_true
189 | pretty_name = lc_json_custom=>pretty_mode-camel_case.
190 |
191 | lv_json_custom = lo_json_custom->serialize_int( data = ls_exp ).
192 | lo_json_custom->deserialize_int( EXPORTING json = lv_json_custom CHANGING data = ls_act ).
193 |
194 | " alternative way
195 | lc_json_custom=>deserialize_ex( EXPORTING json = lv_json_custom CHANGING data = ls_act ).
196 | cl_aunit_assert=>assert_equals( act = ls_act exp = ls_exp msg = 'Custom pretty name fails!' ).
197 |
198 | WRITE / lv_json_custom.
199 | ```
200 | Results in the following JSON:
201 | ```json
202 | {
203 | "triboolXxx": false,
204 | "str1Xxx": "",
205 | "str2Xxx": "ABC",
206 | "initialXxx": 0
207 | }
208 | ```
209 |
210 | # Continue reading
211 | * [Basic usage of the class](basic.md)
212 | * [Advanced Use cases](advanced.md)
213 | * [FAQ](faq.md)
214 | * [Version History](history.md)
215 |
--------------------------------------------------------------------------------
/docs/data-access.md:
--------------------------------------------------------------------------------
1 | # Dynamic Data Accessor Helper Class for ABAP
2 |
3 | # Why
4 | Sometimes you need to access [ABAP data objects dynamically](https://help.sap.com/http.svc/rc/abapdocu_751_index_htm/7.51/en-US/abendyn_access_data_obj_guidl.htm). For example, when:
5 | * you do not know the structure of the ABAP object and to dynamically access the value of the field, you are forced to use ASSIGN .. COMPONENT with field symbol
6 | * you do a cross-release development in ABAP and do not know if some field exists, so you need dynamic access to data
7 | * you access data from the optional ABAP component and also need dynamic access to the ABAP structure field or internal table row
8 | * you need a key or index access to a dynamic internal table
9 |
10 | Normally, if you need to access ABAP fields/structure components dynamically, you will end up coding like this:
11 |
12 | ## Accessing Data Objects Dynamically (as suggested in the documentation)
13 | ```abap
14 | FIELD-SYMBOLS: TYPE ANY TABLE,
15 | TYPE ANY,
16 | TYPE ANY.
17 |
18 | ASSIGN COMPONENT `FIELD1` OF STRUCTURE TO .
19 | IF IS ASSIGNED.
20 | WRITE: .
21 | ENDIF.
22 | ```
23 | That may be OK (however still not very convenient), but if you have a dynamic ABAP object with a deeper structure, it becomes boring:
24 |
25 | ## Accessing Deeply Nested Data Objects Dynamically
26 | ```abap
27 | FIELD-SYMBOLS:
28 | TYPE ANY TABLE,
29 | TYPE ANY,
30 | TYPE ANY,
31 | TYPE ANY,
32 | TYPE ANY,
33 | TYPE ANY.
34 |
35 | ASSIGN COMPONENT `FIELD1` OF STRUCTURE TO .
36 | IF IS ASSIGNED.
37 | ASSIGN COMPONENT `CHILD_1` OF STRUCTURE TO .
38 | IF IS ASSIGNED.
39 | ASSIGN COMPONENT `CHILD_11` OF STRUCTURE TO .
40 | IF IS ASSIGNED.
41 | ASSIGN COMPONENT `CHILD_111` OF STRUCTURE TO .
42 | IF IS ASSIGNED.
43 | WRITE: .
44 | ENDIF.
45 | ENDIF.
46 | ENDIF.
47 | ENDIF.
48 | ```
49 |
50 | And if one thinks about accessing dynamically elements from nested tables it would be at all - hell.
51 | So, below one can find a helper class, which may help in such cases, in a lean and tasty way.
52 | An original and actual version of the source can be found in class /UI2/CL_DATA_ACCESS delivered with UI2 Add-on (can be applied to SAP_BASIS 700 – 76X). So, you can use this ABAP JSON parser in your standard code mostly on any system. Delivered with a note [2526405](https://help.sap.com/http.svc/rc/abapdocu_751_index_htm/7.51/en-US/abendyn_access_data_obj_guidl.htm).
53 |
54 | # What it can
55 | The accessor is a single class, with the following features:
56 | * traversing of any data object (classes are not yet supported) passed as a reference to data or as data.
57 | * traversing nested objects without any level limitations
58 | * automatically resolving reference variables (REF TO fields accessed the same way as standard fields)
59 | * a method like or XPath-like ways of accessing data
60 | * read/modification access to elementary types
61 | * accessing table rows using index or key
62 |
63 | ## Limitations
64 | The code of the dynamic data accessor class does not pretend to be a complete and fully robust solution, but it may become like this if requests come
65 | Current limitations are the following:
66 | * only ABAP data structures are supported. Traversing of ABAP objects/classes not yet supported, however possible
67 | * dynamic modification of tables (not data inside) is not supported. E.g you can not add/remove rows with API, but you can do it via reference to the table
68 | * The syntax for key access of the rows in dynamic tables does not allow usage of the symbol "," as part of the query. No escaping is supported.
69 |
70 | # Usage
71 | ## Necessary type and data declarations for example below
72 | ```abap
73 | TYPES:
74 | BEGIN OF t_properties,
75 | enabled TYPE abap_bool,
76 | length TYPE i,
77 | description TYPE c LENGTH 20,
78 | END OF t_properties,
79 | BEGIN OF t_data,
80 | id TYPE i,
81 | properties TYPE t_properties,
82 | content TYPE string,
83 | END OF t_data,
84 | BEGIN OF t_name_value,
85 | name TYPE string,
86 | value TYPE string,
87 | END OF t_name_value,
88 | BEGIN OF t_example,
89 | flag TYPE abap_bool,
90 | props TYPE t_data,
91 | params TYPE SORTED TABLE OF t_name_value WITH UNIQUE KEY name,
92 | END OF t_example.
93 |
94 | DATA: ls_data TYPE t_example,
95 | lr_data TYPE REF TO data,
96 | lr_ref TYPE REF TO data,
97 | lv_int TYPE i,
98 | lo_data TYPE REF TO /ui2/cl_data_access.
99 |
100 | FIELD-SYMBOLS: TYPE data.
101 |
102 | ls_data-props-id = 12345.
103 | ls_data-props-content = `Some Content`.
104 | ls_data-props-properties-enabled = abap_false.
105 | ls_data-props-properties-length = 10.
106 | ls_data-props-properties-description = `My description`.
107 |
108 | GET REFERENCE OF ls_data INTO lr_data.
109 | ```
110 |
111 | ## Usage examples
112 |
113 | ### Traversing ABAP data dynamically
114 | Simplest usage example:
115 | ```abap
116 | CREATE OBJECT lo_data EXPORTING ir_data = lr_data.
117 |
118 | " standard way (does not work on SAP_BASIS 700)
119 | lr_ref = lo_data->at(`PROPS`)->at(`id`)->ref( ).
120 | IF lr_ref IS BOUND.
121 | ASSIGN lr_ref->* TO .
122 | WRITE: .
123 | ENDIF.
124 | " XPath like
125 | lr_ref = lo_data->at(`PROPS-ID`)->ref( ).
126 | IF lr_ref IS BOUND.
127 | ASSIGN lr_ref->* TO .
128 | WRITE: .
129 | ENDIF.
130 |
131 | " using helper method for creation
132 | lr_ref = /ui2/cl_data_access=>create( ir_data = lr_data iv_component = `PROPS-ID`)->ref( ).
133 | IF lr_ref IS BOUND.
134 | ASSIGN lr_ref->* TO .
135 | WRITE: .
136 | ENDIF.
137 |
138 | " reading value directly
139 | lo_data->at(`props-properties-length`)->value( IMPORTING ev_data = lv_int ).
140 | WRITE: lv_int.
141 | ```
142 |
143 | ### Dynamic data modification in ABAP in a nice way
144 | If you want to use modification operations, you can only operate with references when creating an accessor object.
145 | ```abap
146 | " modifing value
147 | lr_ref = lo_data->at(`props-properties-length`)->ref( ).
148 | ASSIGN lr_ref->* TO .
149 | = 25.
150 |
151 | " or even more simple
152 | lo_data->at(`props-properties-length`)->set( 15 ).
153 | ```
154 |
155 | ### Accessing not existing component
156 | The code is robust - accessing existing components of any level will not result in a crash but will return an empty reference or initial value.
157 | ```abap
158 | " reading not existing value returns the initial value
159 | lo_data->at(`props-properties-not_exist-length-not-exist`)->value( IMPORTING ev_data = lv_int ).
160 | WRITE: lv_int. " -> 0
161 | ```
162 |
163 | ### Working with tables:
164 | Dynamic Access in ABAP in an easy way
165 | ```abap
166 | DATA:
167 | ls_data TYPE t_example,
168 | ls_line LIKE LINE OF ls_data-params,
169 | lv_value TYPE string,
170 | lo_data TYPE REF TO /ui2/cl_data_access.
171 |
172 | ls_line-name = `KEY1`.
173 | ls_line-value = `Value1`.
174 | INSERT ls_line INTO TABLE ls_data-params.
175 |
176 | ls_line-name = `KEY2`.
177 | ls_line-value = `Value2`.
178 | INSERT ls_line INTO TABLE ls_data-params.
179 |
180 | /ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[2]-name`)->value( IMPORTING ev_data = lv_value ).
181 | WRITE: lv_value.
182 |
183 | /ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[name=KEY1]-value`)->value( IMPORTING ev_data = lv_value ).
184 | WRITE: lv_value.
185 |
186 | /ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params`)->at(`[name=KEY1]-value`)->value( IMPORTING ev_data = lv_value ).
187 | WRITE: lv_value.
188 |
189 | /ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[name=KEY1, value=Value1]-value`)->value( IMPORTING ev_data = lv_value ).
190 | WRITE: lv_value.
191 | ```
192 |
193 | # API description
194 |
195 | ## CREATE - Static Method Public Helper method for creating an instance of dynamic accessor
196 | * \> IR_DATA (ref to data) - Importing Type Ref To DATA Reference to data (allows modification of embedded data)
197 | * \> IV_DATA (data) - any data (modification of embedded data not allowed)
198 | * \> IV_COMPONENT (string) - Sub-component name (XPath-like syntax is supported)
199 | * \< RO_REF (ref to /ui2/cl_data_access) - Reference to accessor object pointing to subcomponent
200 |
201 | ## CONSTRUCTOR - Instance Method Public Constructor
202 | * \> IR_DATA (ref to data) - Importing Type Ref To DATA Reference to data (allows modification of embedded data)
203 | * \> IV_DATA (data) - any data (modification of embedded data not allowed)
204 |
205 | ## AT - Instance Method Public Component accessor
206 | * \> IV_COMPONENT (string) - Sub-component name (XPath-like syntax is supported)
207 | * \< RO_REF (ref to /ui2/cl_data_access) - Reference to accessor object pointing to subcomponent
208 |
209 | ## EMPTY - Instance Method Public Returns TRUE if the embedded object is initial (not bound)
210 | * \< RV_VAL (boolean) - ABAP_TRUE if the object is initial and data is not bound
211 |
212 | ## REF - Instance Method Public Returns a reference to the embedded object
213 | * \< RV_DATA (ref to data) - Reference to embedded data
214 |
215 | ## VALUE - Instance Method Public Returns copy of the value
216 | * \< EV_DATA (data) - Copy of the embedded data value, or initial if data is not bound
217 |
218 | ## SET - Instance Method Public Sets the value of the embedded object, if not initial
219 | * \> IV_DATA (data) - New value for embedded object
220 | * \< RV_SUCCESS (boolean) - ABAP_TRUE, if data was successfully modified
221 |
222 | # XPath-like dynamic data access
223 | To access nested components you can use a nice, object-oriented way, using nested calls of AT method (it is robust, and would not crash accessing not existing components), as
224 | ```abap
225 | lo_object->at('subcomp1')->at('subcomp11')->...
226 | ```
227 |
228 | But if you prefer a more compact form, or run code on SAP_BASIS < 702 (nested method calls are not supported), you may use XPath-like syntax for accessing components. Like this:
229 | ```abap
230 | lo_object->at('subcomp1-subcomp11-subcomp111')
231 | ```
232 |
233 | or like this
234 | ```
235 | abap lo_object->at('subcomp1->subcomp11->subcomp111')
236 | ```
237 |
238 | ## The syntax:
239 | * You can use any symbol (or combinations of symbols) as a component separator, except "[", "]", "=", ",". The recommended separator symbol is "-".
240 | * For dynamic index access of rows in nested tables use "[index]" after the component name. E.g. "table_name[2]". The indexing starts from 1. If the accessor object references the table data object directly, you may skip the component name. E.g. "[2]". You may continue accessing components after index access, e.g.: "table_a[1]-struct-table_b[2]". If you do out-of-range access, you get an empty reference back. Index access works only with index tables (STANDARD, SORTED).
241 | * For dynamic key access of rows in nested tables use "(key=value)" for single key lookup, "(key1=value1, key2=value2)" for multi-key lookup, and "(value)" for table line lookup. You can NOT search for values containing ",". You can use nested lookups: "table_a(key1=value1)-table_b(key2=value2, key3=value3)". Values used in a query shall be assignable to field structures.
242 |
243 | # Version History
244 |
245 | ## Note [2798102](https://launchpad.support.sap.com/#/notes/2798102) - PL12
246 | * Fixed. Access to fields with special characters in the name (e.g. "/BIC/YEAR") fails.
247 |
248 | ## Note [2786259](https://launchpad.support.sap.com/#/notes/2786259) - PL11
249 | * Fixed. Short dump, when accessing elements of a null array
250 |
251 | ## Note [2526405](https://launchpad.support.sap.com/#/notes/2526405)
252 | * New: /UI2/CL_DATA_ACCESS class for working with dynamic ABAP data object (generated with method /UI2/CL_JSON=>GENERATE). The class can be used as a replacement for multiple ASSIGN COMPONENT language constructions.
253 |
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | * [It is slow](#it-is-slow)
4 | * [GENERATE or DESERIALIZE into REF TO DATA vs. DESERIALIZE into a typed data structure](#generate-or-deserialize-into-ref-to-data-vs-deserialize-into-a-typed-data-structure)
5 | * [JSON to ABAP data type conversion when using GENERATE or DESERIALIZE into REF TO DATA](#json-to-abap-data-type-conversion-when-using-generate-or-deserialize-into-ref-to-data)
6 | * [Serialize huge data objects into JSON and short dumps](#serialize-huge-data-objects-into-json-and-short-dumps)
7 | * [Encoding of Unicode characters (for example Chinese)](#encoding-of-unicode-characters-for-example-chinese)
8 | * [Incompatible change for initial date/time fields serializing with PL16](#incompatible-change-for-initial-datetime-fields-serializing-with-pl16)
9 | * [Is there a way to deserialize objects that have references to Interface?](#is-there-a-way-to-deserialize-objects-that-have-references-to-interface)
10 | * [Is it possible to have a defined order of fields in ABAP structures generated when deserializing into REF TO DATA fields? Is it possible to have the fields in the generated structure in the same order as in the JSON file?](#is-it-possible-to-have-a-defined-order-of-fields-in-abap-structures-generated-when-deserializing-into-ref-to-data-fields-is-it-possible-to-have-the-fields-in-the-generated-structure-in-the-same-order-as-in-the-json-file)
11 | * [Is it possible to display the currency amount (CURR fields) formatted in the JSON output based on the related currency (CUKY field)?](#is-it-possible-to-display-the-currency-amount-curr-fields-formatted-in-the-json-output-based-on-the-related-currency-cuky-field)
12 | * [My fields are/NOT serialized as true/false instead and serialized like 'X' or ''! E.g. how to control ABAP/JSON Boolean conversion?](#my-fields-arenot-serialized-as-truefalse-instead-and-serialized-like-x-or--eg-how-to-control-abapjson-boolean-conversion)
13 | * [I can not use /UI2/CL_JSON for ABAP Cloud BADi development](#i-can-not-use-ui2cl_json-for-abap-cloud-badi-development)
14 | * [You get a short dump OBJECTS_NOT_CHAR when serializing data with enabled conversion exits](#you-get-a-short-dump-objects_not_char-when-serializing-data-with-enabled-conversion-exits)
15 | * [Why special characters in JSON attribute names are not escaped or unescaped?](#why-are-special-characters-in-json-attribute-names-not-escaped-or-unescaped)
16 |
17 | ## It is slow
18 | It is as fast as possible to achieve it in pure ABAP and already heavily optimized. If you have suggestions on how to make it faster - you are welcome. Features like type conversions, type detections, renaming, data generation, etc require processing time, and even if they are not active you may pay the penalty because the class design allows this feature. Operations on strings are not fast in ABAP, and method calls are costly, which is why macros are used within the class. However, the class is robust and can handle any data type for serialization and deserialization, offering many convenient functions that would otherwise need to be implemented manually. It performs well in numerous use cases.
19 |
20 | In scenarios where extensive functionality and flexibility are unnecessary, such as with simple, flat tables requiring fast JSON serialization/deserialization, tweaking /ui2/cl_json for speed by disabling flags is not viable. No other universal classes with comparable functionality are known to be faster. The best solutions currently are:
21 |
22 | * **CALL TRANSFORMATION id**: This is the fastest alternative (10x), but it requires accepting a proprietary JSON format with missing type conversions and UPPER case attribute names. It can be suitable if you control the other side and can parse the proprietary JSON.
23 | * **CALL TRANSFORMATION with custom XSLT**: This option is slower (~7x, depending on XSLT complexity) but allows control over the JSON format through custom XSLT, including attribute names and values. However, it works only with ABAP component names and values, without RTTI information. You need to write, deliver, and synchronize your XSLT with your data structures.
24 |
25 | For more details, please refer to the documentation: [JSON to ABAP transformation with the use of CALL TRANSFORMATION](https://github.com/SAP/abap-to-json/blob/main/docs/advanced.md#json-to-abap-transformation-with-the-use-of-call-transformation)
26 |
27 | ## GENERATE or DESERIALIZE into REF TO DATA vs. DESERIALIZE into a typed data structure
28 | It is always better to deserialize into explicit data structure but not into anonymous reference:
29 |
30 | 1. It is faster
31 | 2. It is type-safe
32 | 3. Processing deserialized results is much easier.
33 | Deserializing into REF TO data is the same as using the GENERATE method and results in generating real-time ABAP types, which is quite slow. You can not specify the resulting types for data elements and the deserializer needs to guess types. To process generated results, you always use dynamic programming, which is by default slow (or [/UI2/CL_DATA_ACCESS](https://github.com/SAP/abap-to-json/blob/main/docs/data-access.md), which is more comfortable but still uses dynamic programming inside).
34 |
35 | ## JSON to ABAP data type conversion when using GENERATE or DESERIALIZE into REF TO DATA
36 | The data type selection logic of the GENERATE method (DESERIALIZE into REF TO data) is not guaranteed or defined. The class makes the best guess for the resulting ABAP data type based on the JSON value and the best-fitting data type on the ABAP side. For example, JSON booleans convert to ABAP_BOOL, JSON numbers can convert to I, P, or F types depending on the value, and JSON strings convert to date, time, or timestampl if the value matches a pattern, otherwise, they convert to a string. A new type of conversion may be introduced in the future. If you do not provide a fixed ABAP structure, you must be prepared to work with any of the generated types. Additionally, if you use the GEN_OPTIMIZE flag, you may receive direct types instead of references. For explicit data types, deserialize into a fixed structure.
37 |
38 | ## Serialize huge data objects into JSON and short dumps
39 | You are using the class to serialize your data into JSON. Unfortunately, sometimes you pass too big tables, which results in too long a JSON string (for example, longer than 1GB), and this leads to dumps like SYSTEM_NO_ROLL, STRING_SIZE_TOO_LARGE, MEMORY_NO_MORE_PAGING, while the system can not allocate such a big continuous memory chunk. This specific case could be solved by increasing the memory allocation limit, but you would still end up with an INT4 size limit for string length, which can not be more than 2GB.
40 |
41 | The string (JSON) of such size can not be created, transported, or persisted. You would need to have special handling on your side for this case.
42 | E.g. if you want to serialize such a big amount of data, you will need to split the input into chunks and do the serialization and transport of the resulting JSON chunks by parts.
43 |
44 | The memory exceptions are not catchable and you will need to do data size evaluations on your side, before calling serialization.
45 |
46 | So, the only robust way to solve the issue will be by limiting serialized data size, which can be done only on the /ui2/cl_json consumer side.
47 |
48 | Even if you select another format for serialization (XML or ABAP JSON) you will stick to some limit. So, no other way.
49 |
50 | If you still need to serialize everything, you may split data into chunks and give them to the serializer one by one. Then deserialize all fragments into the same data object for merging.
51 |
52 | ## Encoding of Unicode characters (for example Chinese)
53 | The serializer does not do any explicit character encoding, this is done by ABAP. Normally, ABAP works with UTF16, a 2-byte Unicode encoding that can represent any character (also Chinese). That is why you see Chinese characters in the debugger. Later on, after serializing in JSON (you may also check in debugger JSON and see that Chinese characters are still in), you pass the JSON string further, maybe as a REST response. And there is, probably, converted into UTF8 encoding, which is multibyte encoding, where some characters (Latin) are encoded with one byte and some (Chinese, Russian, etc.) as multibyte. Then the viewer of such UTF8 text shall be able to interpret and display them properly. If you do not see characters as expected in your viewer tool, then, probably, nothing is corrupt and the receiver will get them fine. It is just an issue of the viewer that does not recognize UTF8, or probably, lost an encoding ID interpreted wrong.
54 |
55 | ## Incompatible change for initial date/time fields serializing with PL16
56 | First of all, I would agree that this is an incompatible change and I am asking you to excuse me for your efforts. It was an intentional change and I was aware that someone can already rely on current behavior and may get issues.
57 |
58 | The reason for this change of default was a customer complaint regarding the handling of initial date-time values, which are not 0000-00-00 or 00:00:00. In general 0000-00-00 is an invalid date, 00:00:00 is valid, but how to understand that it is initial but not explicit midnight?
59 |
60 | Because of that, I have decided not to render initial values for date/time and give a receiver a way to understand that it is initial and has its default/initial processing. I know that it is incompatible, but I want a default behavior to be the best and most common choice, even with the cost of modification of the consumer code that relies on old behavior :/.
61 |
62 | Because having custom rendering of the initial date/time is quite exotic, I have only let this for constructor calls and have not extended the serialize method, to keep standard API simple. If I get multiple requests regarding extending SERIALIZE with these defaults - I will do it.
63 |
64 | My recommendation for you:
65 | * Variant 1: Adopt your unit tests for new initial values for date-time.
66 | * Variant 2: Use the instance method for serialization. E.g. parametrized CONSTRUCTOR + SERIALIZE_INT (in this case you can customize behavior by parametrizing instance constructor with desired initial values for time date).
67 | * Variant 2: Extend the /ui2/cl_json class or create a helper method in your class with your static customized SERIALIZE call which already considers new defaults for the /ui2/cl_json constructor.
68 |
69 | ## Is there a way to deserialize objects that have references to Interface?
70 | **Q**: I am using /ui2/cl_json to serialize an object that contains some reference attributes. These reference attributes are TYPE REF TO . Upon deserialization, the references are not getting deserialized. Is there a way to deserialize objects that have references to Interface?
71 |
72 | **A**: Unfortunately - not. To deserialize an object, it shall be created. And how would you like to create an instance of the interface without knowing the class? It can not be done automatically. But you may try to [implement the deserialization logic by yourself](docs/advanced.md#jsonabap-serializationdeserialization-with-runtime-type-information).
73 |
74 | ## Is it possible to have a defined order of fields in ABAP structures generated when deserializing into REF TO DATA fields? Is it possible to have the fields in the generated structure in the same order as in the JSON file?
75 | The order of fields in JSON and also in ABAP is undefined. It may happen that you will have two records of the same type in an array but with attributes serialized in different orders.
76 | What to do in this case? In general, the answer is – no (there is no way to configure it). The current alphabetical order gives at least some predefined output (but the result is a name normalization and uniqueness check).
77 |
78 | If you want a specific order, just deserialize it in a predefined structure, but not in REF TO DATA. Generating into REF TO DATA is always a bad choice (from a performance and type definition perspective).
79 |
80 | But there is not an easy way, in the latest releases of the class.
81 | If you still want a predefined sequence of the fields in the generated structure, you may inherit the class, and prefill structure buffer (mt_struct_type) in your inherited class constructor. See method GENERATE_STRUCT for details. In this case, later deserialize/generate calls will use your structure type, but not one created with default logic.
82 |
83 | ## Is it possible to display the currency amount (CURR fields) formatted in the JSON output based on the related currency (CUKY field)?
84 | No, there is no built-in support for currency fields. Potentially one can add it in a derived class, overwriting dump_int and restore_type methods, but I do not want to have it in by default, because of implementation complexity and performance penalty.
85 |
86 | Only single-field conversion exits are supported.
87 |
88 | ## My fields are/NOT serialized as true/false instead and serialized like 'X' or ''! E.g. how to control ABAP/JSON Boolean conversion?
89 | JSON, as JavaSctript, has a built-in Boolean type with true/false values. ABAP does not have a built-in Boolean type and uses fields of char 1 with constant values of 'X' (TRUE) and ''(space, FALSE). Different teams use different predefined types to be used as a Boolean type for them. It is a zoo. There is no way to detect the boolean type or even be able to auto-convert them between different ABAP types. However, there are some more or fewer standard conventions for which standard types shall be used for booleans. The serializer class has the default list of standard boolean types hardcoded in constant MC_BOOL_TYPES (ABAP_BOOLEAN, ABAP_BOOL, BOOLEAN, BOOLE_D, XFELD, XSDBOOLEAN, WDY_BOOLEAN). If you use one of these types in your data, passed to the serializer, it will be automatically processed by the parser and converted from ''/'X' into false/true and vice versa. If you use any other type not in the list, there will be no auto-conversion. OK, you do not like default (it processes too many types or two less), what do you do? You have the following choices:
90 | * Do not use static methods for serialization and deserialization, but instance ones (e.g. json_obj->serilaize_int instead of /ui2/cl_json=>serilaize) AND customize the behavior of the instance by constructor parameters. You can pass an alternative set of boolean types with the parameter BOOL_TYPES. The usage of instance methods is also faster if you repeat calls for serialization.
91 | * Inherit the class and overwrite default boolean types, stored in the class variable mv_bool_types with your preferred default. Use your class everywhere instead of standard, to ensure consistency. How to inherit the class you can find [here](docs/class-extension.md).
92 |
93 | ## I can not use /UI2/CL_JSON for ABAP Cloud BADi development
94 | The class has been released for ABAP Cloud development (Steampunk) for a long time. Initially, it was released only for the Public Cloud, and the Private Cloud was missed by mistake. That was corrected and now you can use it for Private Cloud from release OP 2023 (SAP_BASIS 758). See details [here](docs/history.md#note-3424850-ui2cl_json-release-api-for-cloud-development). If you need JSON processing in ABAP Cloud BADis, you may need to use the XCO library (XCO_JSON), which is meant to be the official JSON processing library for Key User Extensibility Apps. If you still think that the use of /UI2/CL_JSON would be preferable you may ask for releasing of it via the Customer Influence program, like it was [done for Steampunk sometime](https://influence.sap.com/sap/ino/#/idea/234724/?section=sectionVotes).
95 |
96 | ## You get a short dump OBJECTS_NOT_CHAR when serializing data with enabled conversion exits
97 | You can apply conversion exits to serialized data when using the /ui2/cl_json. The class uses a temporary buffer of type STRING as an output for conversion exits. But some old conversion exits support only writing in char-like variables (C LENGHT ...) (restriction of WRITE TO) and dumping when WRITE TO is executed with STRING output. But the current [programming guidelines for conversion exits](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenconversion_exits.htm) say that output can be [C-LIKE type](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenbuilt_in_types_generic.htm) (c, n, and string, as well as the date/time types d, t and character-like flat structures). So it can be a string.
98 | If you get such a dump, please raise a message and ask the conversion exit owner to update the code to support STRING type, following the example implementation for CONVERSION_EXIT_SDURA_OUTPUT from SAP Help]:
99 | ```abap
100 | FUNCTION CONVERSION_EXIT_SDURA_OUTPUT.
101 | *"----------------------------------------------------------------------
102 | *"*"Local Interface:
103 | *" IMPORTING
104 | *" VALUE(INPUT)
105 | *" EXPORTING
106 | *" VALUE(OUTPUT) TYPE CLIKE
107 | *"----------------------------------------------------------------------
108 |
109 | hours = input DIV 60.
110 | minutes_n = input MOD 60.
111 |
112 | DESCRIBE FIELD output TYPE DATA(typ).
113 |
114 | IF typ = 'g'. "OUTPUT is type string, no WRITE TO for strings, enabled with string templates 20130423, KELLERH
115 | output = |{ hours WIDTH = 3 ALIGN = RIGHT }:{ minutes_n WIDTH = 2 }|.
116 | ELSE. "Old overflow behavior to stay compatible
117 | WRITE hours TO output(3) NO-SIGN.
118 | output+3(1) = ':'.
119 | WRITE minutes_n TO output+4(2).
120 | ENDIF.
121 | ENDFUNCTION.
122 | ```
123 | Use of output buffer TYPE C LENGHT ... in code of /ui2/cl_json would require an additional CONDENSE call that would negatively impact the performance of serialization and may still lead to incorrect data rendering (the logic with TYPE C LENGHT... was in PL19, but is reverted with PL 20, because on [this issue](issues/10)).
124 |
125 | ## Why are special characters in JSON attribute names not escaped or unescaped?
126 | This is a known limitation. Escaping, and especially unescaping, is very performance-critical and will significantly influence parsing time. Cases where attribute names contain special characters are quite unique — ABAP field names do not allow special characters. So, to optimize overall performance, I have decided not to support this. The only cases when the parser does escaping and unescaping the attribute names are usages of the ASSOC_ARRAYS flag when table key values are converted into leading attribute names (associative arrays in terms of JSON) and generation of the structures (internally it also uses ASSOC_ARRAY flag).
127 |
128 | # Continue reading
129 | * [Basic usage of the class](basic.md)
130 | * [Advanced Use cases](advanced.md)
131 | * [Version History](history.md)
132 |
--------------------------------------------------------------------------------
/docs/history.md:
--------------------------------------------------------------------------------
1 | # Version History
2 | * [Note 3568088 - PL22](#note-3615316---pl22)
3 | * [Note 3568088 - PL21](#note-3568088---pl21)
4 | * [Note 3515438 - PL20](#note-3515438---pl20)
5 | * [Note 3414589 - PL19](#note-3414589---pl19)
6 | * [Note 3424850 - /UI2/CL_JSON - Release API for Cloud Development](#note-3424850---ui2cl_json---release-api-for-cloud-development)
7 | * [Note 3315430 - PL18](#note-3315430---pl18)
8 | * [Note 3106267 - PL17](#note-3106267---pl17)
9 | * [Note 3038042 - PL16](#note-3038042---pl16)
10 | * [Note 2944398 - PL15](#note-2944398---pl15)
11 | * [Note 2904870 - PL14](#note-2904870---pl14)
12 | * [Note 2870163 - PL13](#note-2870163---pl13)
13 | * [Note 2798102 - PL12](#note-2798102---pl12)
14 | * [Note 2786259 - PL11](#note-2786259---pl11)
15 | * [Note 2763854 - PL10](#note-2763854---pl10)
16 | * [Note 2650040](#note-2650040)
17 | * [Note 2629179](#note-2629179)
18 | * [Note 2526405](#note-2526405)
19 | * [Note 2292558](#note-2292558)
20 | * [Note 2300508](#note-2300508)
21 | * [Note 2330592](#note-2330592)
22 | * [Note 2368774](#note-2368774)
23 | * [Note 2382783](#note-2382783)
24 | * [Note 2429758](#note-2429758)
25 | * [Note 2480119](#note-2480119)
26 |
27 | ## Note [3615316](https://launchpad.support.sap.com/#/notes/3615316) - PL22 (not released)
28 | ### /UI2/CL_JSON
29 | * Fixed: Bug with generation of the structures with similar field names (fex, "a", "bc" vs "ab", "c").
30 |
31 | ## Note [3568088](https://launchpad.support.sap.com/#/notes/3568088) - PL21
32 | ### /UI2/CL_JSON
33 | * Fixed: The wrong elementary type is used when generating data for REF TO data (TIMESTAMP instead of string)
34 | * Fixed: attribute names created from table key values are not escaped/unescaped when using the ASSOC_ARRAYS option.
35 | * Fixed: additional performance optimization for deserializing long strings with escaped characters. Because of the use of CALL TRANSFORMATION id for JSON, it requires kernel updates for SAP_BASIS 7.02 and 7.31 (SAP Notes 1648418 and 1650141).
36 |
37 | ## Note [3515438](https://launchpad.support.sap.com/#/notes/3515438) - PL20
38 | ### /UI2/CL_JSON
39 | * Fixed: Empty objects ( "a":{} ) are not initialized when using GENERATE or DESERIALIZE into REF TO data, taking the value of the previous field
40 | * Added: Support for PascalCase for pretty printing was added.
41 | * Fixed. The logic for OUTPUT conversion exits reverted to the state of PL18 ([details](https://github.com/SAP/abap-to-json/issues/10)).
42 |
43 | ## Note [3414589](https://launchpad.support.sap.com/#/notes/3414589) - PL19
44 | ### /UI2/CL_JSON
45 | * Fixed: enhanced processing of deserialization into typed TYPE REFs
46 | * Fixed: deserialization of JSON 'null' into complex, not reference ABAP fields does not lead to exception in strict mode ([details](https://github.com/SAP/abap-to-json/pull/5))
47 | * Fixed: Support for ABAP_BOOLEAN type was added.
48 | * Fixed: performance optimization for deserializing strings with escaped line breaks and special characters. When you have escaped "\\", e.g., "\\\\", it is still slow.
49 | * Fixed: short dump in conversion exits routines on SERIALIZE/DESERIALIZE because of the wrong data type (OBJECTS_NOT_CHAR).
50 | * Fixed. Added support for the timezone offsets for ISO8601.
51 | * Fixed: rounding bug when deserializing timestamps with sub-seconds into short timestamps (seconds)
52 | * New: Information about the invalid field added to the exception data of move_cast_error ([details](https://github.com/SAP/abap-to-json/pull/8))
53 | * New: Serialization now can detect "timestamps" defined with the help of data domains ([details](https://github.com/SAP/abap-to-json/pull/9))
54 | * New: new switch for GENERATE (optimize) and DESERIALIZE (gen_optimize) that enables optimization of generated ABAP data for REF TO DATA (fewer references, easily readable and accessible). Results in longer processing.
55 | * New: added detection of time, date, and timestamp values on generation/deserialization into TYPE REF TO data.
56 |
57 | ## Removed dependency to /ui2/cl_data_access for z_ui2_json
58 | * Added: Open Source version of the /ui2/cl_data_access as Z_UI2_DATA_ACCESS
59 |
60 | ## Note [3424850](https://launchpad.support.sap.com/#/notes/3424850) - /UI2/CL_JSON - Release API for Cloud Development
61 | You can not use the class in the Private Cloud, because the class has not been released for Cloud Development (for the Public Cloud, it was already released).
62 | Delivered with OP 2025 and OP 2023 FPS2. Note for OP 2023 (SAP_BASIS 758)
63 |
64 | ## Note [3315430](https://launchpad.support.sap.com/#/notes/3315430) - PL18
65 | ### /UI2/CL_JSON
66 | * Fixed: handling of cycle references when serializing data and object references. The serialization will stop processing of the reference if it is already in the serialization stack.
67 | * Fixed: performance by serialization of timestamps and UTCLONG fields ([details](https://github.com/SAP/abap-to-json/issues/4)).
68 | * Fixed: performance in class-constructor.
69 | * Fixed: deserialization into typed TYPE REF does not work as expected - always the generic GENERATE approach is used.
70 | * Fixed: null references generated as ABAP_BOOL types
71 |
72 | ## Note [3106267](https://launchpad.support.sap.com/#/notes/3106267) - PL17
73 | ### /UI2/CL_JSON
74 | * Fixed: added support for XSDBOOLEAN Boolean type, to be consistent with CALL TRANSFORMATION id rules.
75 | * Fixed: added support for UTCLONG. This new built-in ABAP data type comes with SAP_BASIS 7.54 and adds native support for timestamps to ABAP. The type is always represented in ISO8601 and does not depend on the TS_AS_ISO8601 parameter.
76 | * Fixed: serialization of the timestamp fields into ISO8601 does not add any more sub-second sections into JSON, while it is always initial for timestamps.
77 | * Fixed: deserialization of the JSON strings with non-breakable spaces (nbsp)
78 | * Fixed: processing of the JSON attribute names with escaped double quotes ("abc efg \\\" etc": "value")
79 | * Fixed: deserialization (generation without typed output structure) does not consider exponential numeric values and tries to transform them into an integer and fails, resulting in 0.
80 |
81 | ### New language features used:
82 | * escape function => min requirement SAP_BASIS 7.31
83 | * find_any_not_of function => min requirement SAP_BASIS 7.02
84 | * FIND/REPLACE PCRE => min requirement SAP_BASIS 7.55 (CE 2008/OP 2020) - delayed
85 |
86 | ## Note [3038042](https://launchpad.support.sap.com/#/notes/3038042) - PL16
87 | ### /UI2/CL_JSON
88 | * Fixed: Serializing of hash tables with empty values and the parameters assoc_arrays_opt and compress produces invalid JSON.
89 | * Fixed: Method serialize produces corrupt data when an initial date or time field is serialized (something like "--" or "::" ).
90 | * Fixed: Performance for serializing and deserializing dynamic data objects (REF TO DATA) is greatly improved.
91 | * New: You can apply text formatting/beautifying to serialized JSON with the new parameter format_output.
92 | * New: You can control the format of how hex values are serialized or deserialized with the new parameter hex_as_base64 for the SERIALIZE and DESERIALIZE methods. If hex_as_base64 is set to abap_true, binary/hex values are processed as base64 (default, compatible behavior). If hex_as_base64 is set to abap_false value is processed in raw hex form.
93 | * New: Now you can provide bool types (true, false), 3 bool types (true, false, undefined), initial timestamp, initial time, and initial date (JSON value for initial ABAP value) in the instance constructor. This would allow you to not inherit class but just parametrize (better performance).
94 |
95 | ## Note [2944398](https://launchpad.support.sap.com/#/notes/2944398 ) - PL15
96 | ### /UI2/CL_JSON
97 | * Fixed. Generating ABAP structures for JSON attributes that include special characters like "/\:;~.,-+=><|()[]{}@+\*?!&$#%^'§\`" fails.
98 | * Fixed. Edm.Guid with the mixed or lowercase is not recognized.
99 | * Fixed. Iterating and data assignments for the generated data object (REF TO DATA), produced by the GENERATE and DESERIALIZE methods, fail in ABAP Cloud.
100 |
101 | ### /UI2/CL_DATA_ACCESS
102 | * Fixed. Index access to generated tables in LOOP construction fails.
103 |
104 | ## Note [2904870](https://launchpad.support.sap.com/#/notes/2904870 ) - PL14
105 | ### /UI2/CL_JSON
106 | * Fixed. Unescaping of strings with a single Unicode entity (e.g., "\uXXXX") does not work
107 | * New. More robust logic for handling invalid JSON (e.g, cases with extra "," without further element { "a": 1, } )
108 |
109 | ## Note [2870163](https://launchpad.support.sap.com/#/notes/2870163 ) - PL13
110 | ### /UI2/CL_JSON
111 | * Fixed. Conversion exists and does not work when data is located inside internal tables.
112 | * Fixed. TIMESTAMPL subsecond values are truncated when deserializing from Edm.DateTime.
113 |
114 | ## Note [2798102](https://launchpad.support.sap.com/#/notes/2798102 ) - PL12
115 | ### /UI2/CL_JSON
116 | * New. DESERIALIZE and GENERATE methods supporting decoding of Unicode symbols (\u001F)
117 | * Fixed. Invalid JSON causing exception and dump.
118 | ### /UI2/CL_DATA_ACCESS
119 | * Fixed. Access to fields with special characters in the name (e.g., "/BIC/YEAR") fails.
120 |
121 | ## Note [2786259](https://launchpad.support.sap.com/#/notes/2786259 ) - PL11
122 | ### /UI2/CL_JSON
123 | * Optimized. Performance lost, introduced in PL10 (note 2763854) when unescaping special characters (\r\n\t\")
124 | * Fixed. Short dump, with when running the GENERATE method with empty or invalid input
125 |
126 | ### /UI2/CL_DATA_ACCESS
127 | * Fixed. Short dump, when accessing elements of a null array
128 |
129 | ## Note [2763854](https://launchpad.support.sap.com/#/notes/2763854) - PL10
130 | ### /UI2/CL_JSON
131 | * Fixed: Deserialization and generation of the ABAP data from JSON strings with Unicode characters fail
132 | * Fixed: Unescaping of \\t and \\n char combinations in strings handled incorrectly
133 | * Fixed: GENERATE method fails on JSON attribute names containing spaces
134 |
135 | ## Note [2650040](https://launchpad.support.sap.com/#/notes/2650040)
136 | ### /UI2/CL_JSON
137 | * New: Support for deserialization of OData Edm.Guid
138 | * New: Support for Enum data types in ABAP. From SAP_BASIS 7.51, below, the enums are ignored.
139 | * New: Support for conversion exits for serializing and deserializing.
140 | * Fixed: SERIALIZE method delivers an invalid JSON string when NUMC type, filled with spaces, is used.
141 |
142 | ## Note [2629179](https://launchpad.support.sap.com/#/notes/2629179)
143 | ### /UI2/CL_JSON
144 | * New: JSON timestamp fields, serialized in OData Edm.DateTime format (e.g. "\/Date(1467981296000)\/") is supported, and properly deserialized in ABAP date, time, or timestamp fields
145 | * New: JSON timestamp fields, serialized in OData Edm.Time format (e.g., "PT10H34M55S") is supported, and properly deserialized in ABAP date, time, or timestamp fields
146 | * Fixed: content is scrambled when using the GENERATE method for JSON objects with a name containing special characters (for example "__metadata")
147 | * Fixed: GENERATE method does not consider custom name mapping pairs passed as a parameter for CONSTRUCTOR or GENERATE methods
148 | * Fixed: generation of very long integers (serialized numeric date) fails, due to I type overflow (you get 0 instead of the expected number)
149 |
150 | ## Note [2526405](https://launchpad.support.sap.com/#/notes/2526405)
151 | ### /UI2/CL_JSON
152 | * Fixed: Deserialization of the inconsistent data (JSON string into ABAP table) leads to a short dump if the JSON string is empty.
153 | * Fixed: Serialization of data with includes defined suffix (RENAME WITH SUFFIX) dumps
154 | * Fixed: GENERATE method fails if the JSON object contains duplicate attributes and PRETTY_MODE-CAMEL_CASE is not used.
155 | * Fixed: GENERATE method fails if JSON object contains attribute names longer than 30 characters (allowed ABAP field length). This can also occur in case the name is shorter than 30 characters, but PRETTY_MODE-CAMEL_CASE is used.
156 | * New: methods DUMP_INT, DUMP_TYPE, RESTORE_TYPE, and RESTORE can be overridden now. So, you can introduce your data type conversion on serialization and deserialization.
157 | * New: now it is possible to pass the name mapping table as a parameter for the constructor/serialize/deserialize and control the way JSON names are formatted/mapped to ABAP names. This may help if you need special rules for name formatting (for special characters or two long JSON attributes) and standard pretty printing modes cannot help. With this feature, you may eliminate the need for the class extension and redefine the PRETTY_NAME and PRETTY_NAME_EX methods.
158 | * New: The PRETTY_NAME_EX method was extended to support the encoding of more special characters (characters needed in JSON names but can not be used as part of the ABAP name). The supported characters are: "!#$%&\*-~/:|@.". Used with pretty_mode-extended.
159 |
160 | ### /UI2/CL_DATA_ACCESS
161 | * New: /UI2/CL_DATA_ACCESS class for working with dynamic ABAP data object (generated with method /UI2/CL_JSON=>GENERATE). The class can be used as a replacement for multiple ASSIGN COMPONENT language constructions.
162 |
163 | ## Note [2292558](https://launchpad.support.sap.com/#/notes/2292558)
164 | * Fixed: Empty JSON objects, serialized as entries of the table, are not deserialized into corresponding ABAP structures, and further parsing of the JSON string after an empty object is skipped.
165 | * Fixed: JSON fields containing stringified timestamp representation in ISO 8601 format are not deserialized properly in the corresponding ABAP timestamp field.
166 |
167 | ## Note [2300508](https://launchpad.support.sap.com/#/notes/2300508)
168 | * Fixed: Recursive (hierarchical) JSON objects cannot be deserialized.
169 |
170 | ## Note [2330592](https://launchpad.support.sap.com/#/notes/2330592)
171 | * Fixed: Partial serialization/deserialization of the JSON is not supported
172 | * New: Extending the class is supported
173 | * New: Added support for serializing named include structures from ABAP as embedded sub-objects in JSON
174 |
175 | ## Note [2368774](https://launchpad.support.sap.com/#/notes/2368774)
176 | * Fixed: /UI2/CL_JSON creates unnecessary wrapping JSON object around value for name/value (table with 2 fields), tables with 1 unique key
177 | * Fixed: Performance of serialization/deserialization of big tables into/from JSON associative arrays (maps) is slow
178 | * Fixed: When trying to deserialize an invalid (not matching) structure from JSON to ABAP dump OBJECTS_MOVE_NOT_SUPPORTED occurs
179 |
180 | ## Note [2382783](https://launchpad.support.sap.com/#/notes/2382783)
181 | * Fixed: Unescape of symbol '\' on JSON deserialization does not work
182 | * Fixed: Short dump on serialization of classes with protected/private attributes
183 | * Fixed: Short dump when serializing dynamic, non-existing types
184 |
185 | ## Note [2429758](https://launchpad.support.sap.com/#/notes/2429758)
186 | * Fixed: Short Dump on deserialization of classes with read-only attributes
187 | * New: The serialization parameter was added to NUMC_AS_STRING, controlling the way NUMC fields are serialized. The default is FALSE. If set to TRUE, NUMC fields are serialized not as numbers, but as strings, with all leading zeroes. Deserialization works compatibly with both ways of NUMC serialized data.
188 | * New: GENERATE and GENERATE_INT methods are added for the on-the-fly creation of ABAP data objects from JSON, without the need to have a predefined ABAP structure. Supports automatic creation of ABAP structures, tables, and elementary types, concerning JSON types. Supports structure/table nesting.
189 | * New: DESERIALIZE_INT method throws an exception CX_SY_MOVE_CAST_ERROR and stops further processing in case of malformed data found, and the STRICT_MODE parameter in the constructor is set to TRUE.
190 | * New: Support for XSTRING was added as input for deserialization.
191 |
192 | ## Note [2480119](https://launchpad.support.sap.com/#/notes/2480119)
193 | *Last note with support of SAP_BASIS 740*
194 | * New: GENERATE method creates a local custom class for deserialization (lc_json_custom), instead of standard /ui2/cl_json
195 | * Fixed: Internal tables are not initialized when deserializing JSON with empty arrays
196 | * New: Deserialization into a field with REF TO data type, if the field is bound, using a referenced data type
197 | * New: Deserialization uses automatic generation of the data if the field has a "REF TO DATA" type and the bound data is initial
198 |
--------------------------------------------------------------------------------
/docs/images/generate_sbook1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/abap-to-json/2e150ae959695555d45fae70b470f56298e560c2/docs/images/generate_sbook1.png
--------------------------------------------------------------------------------
/docs/images/generate_sbook2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/abap-to-json/2e150ae959695555d45fae70b470f56298e560c2/docs/images/generate_sbook2.png
--------------------------------------------------------------------------------
/docs/images/generate_sbook_optimized.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/abap-to-json/2e150ae959695555d45fae70b470f56298e560c2/docs/images/generate_sbook_optimized.png
--------------------------------------------------------------------------------
/src/package.devc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Open Source version of /UI2/CL_JSON
7 | X
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/z_ui2_data_access.clas.abap:
--------------------------------------------------------------------------------
1 | "! Helper Class for /UI2/CL_JSON
2 | "! Usage examples and documentation can be found on https://github.com/SAP/abap-to-json
3 | class Z_UI2_DATA_ACCESS definition
4 | public
5 | create public .
6 |
7 | public section.
8 | type-pools ABAP .
9 |
10 | class-methods CLASS_CONSTRUCTOR .
11 | class-methods CREATE
12 | importing
13 | !IR_DATA type ref to DATA optional
14 | !IV_DATA type DATA optional
15 | !IV_COMPONENT type STRING optional
16 | returning
17 | value(RO_REF) type ref to Z_UI2_DATA_ACCESS .
18 | methods CONSTRUCTOR
19 | importing
20 | !IR_DATA type ref to DATA optional
21 | !IV_DATA type DATA optional .
22 | methods AT
23 | importing
24 | !IV_COMPONENT type STRING optional
25 | returning
26 | value(RO_REF) type ref to Z_UI2_DATA_ACCESS .
27 | methods EMPTY
28 | returning
29 | value(RV_VAL) type ABAP_BOOL .
30 | methods REF
31 | returning
32 | value(RV_DATA) type ref to DATA .
33 | methods VALUE
34 | importing
35 | !IV_DEFAULT type DATA optional
36 | exporting
37 | !EV_DATA type DATA .
38 | methods SET
39 | importing
40 | !IV_DATA type DATA
41 | returning
42 | value(RV_SUCCESS) type ABAP_BOOL .
43 | methods COUNT
44 | returning
45 | value(RV_LINES) type I .
46 | protected section.
47 |
48 | data MR_DATA type ref to DATA .
49 |
50 | class-methods DEREF
51 | importing
52 | !IR_DATA type ref to DATA
53 | returning
54 | value(RR_DATA) type ref to DATA .
55 | methods AT_INT
56 | importing
57 | !IV_COMPONENT type STRING optional
58 | !IV_INDEX type I optional
59 | !IV_KEYS type STRING optional
60 | returning
61 | value(RO_REF) type ref to Z_UI2_DATA_ACCESS .
62 | private section.
63 |
64 | class-data SO_REGEX_HIER type ref to CL_ABAP_REGEX .
65 | class-data SO_REGEX_KEYS type ref to CL_ABAP_REGEX .
66 | ENDCLASS.
67 |
68 |
69 |
70 | CLASS Z_UI2_DATA_ACCESS IMPLEMENTATION.
71 |
72 |
73 | METHOD AT.
74 |
75 | DATA:
76 | lv_component TYPE string,
77 | lv_sindex TYPE string,
78 | lv_keys TYPE string,
79 | lv_index TYPE i,
80 | lt_hier TYPE match_result_tab.
81 |
82 | FIELD-SYMBOLS:
83 | LIKE LINE OF lt_hier,
84 | TYPE LINE OF submatch_result_tab.
85 |
86 | " (?:(\w+)|^)(?:\[(?:(\d+)|([^\]]+))\])? - no check for separators
87 | FIND ALL OCCURRENCES OF REGEX so_regex_hier IN iv_component RESULTS lt_hier.
88 |
89 | ro_ref = me.
90 |
91 | LOOP AT lt_hier ASSIGNING .
92 | CHECK ro_ref->empty( ) EQ abap_false.
93 | READ TABLE -submatches INDEX 1 ASSIGNING .
94 | IF -length IS INITIAL.
95 | CLEAR lv_component.
96 | ELSE.
97 | lv_component = iv_component+-offset(-length).
98 | TRANSLATE lv_component TO UPPER CASE.
99 | ENDIF.
100 | READ TABLE -submatches INDEX 2 ASSIGNING .
101 | IF -length IS INITIAL.
102 | CLEAR lv_index.
103 | ELSE.
104 | lv_index = lv_sindex = iv_component+-offset(-length).
105 | ENDIF.
106 | READ TABLE -submatches INDEX 3 ASSIGNING .
107 | IF -length IS INITIAL.
108 | CLEAR lv_keys.
109 | ELSE.
110 | lv_keys = iv_component+-offset(-length).
111 | ENDIF.
112 | ro_ref = ro_ref->at_int( iv_component = lv_component iv_index = lv_index iv_keys = lv_keys ).
113 | ENDLOOP.
114 |
115 | ENDMETHOD. "at_int
116 |
117 |
118 | METHOD AT_INT.
119 |
120 | DATA: lv_key TYPE string,
121 | lv_value TYPE string,
122 | lt_keys TYPE match_result_tab,
123 | lr_res TYPE REF TO data,
124 | lr_data TYPE REF TO data,
125 | lo_type TYPE REF TO cl_abap_typedescr.
126 |
127 | FIELD-SYMBOLS: TYPE data,
128 | TYPE data,
129 | LIKE LINE OF lt_keys,
130 | TYPE LINE OF submatch_result_tab,
131 | TYPE ANY TABLE,
132 | TYPE INDEX TABLE.
133 |
134 | IF mr_data IS BOUND.
135 | IF iv_component IS NOT INITIAL.
136 | ASSIGN mr_data->* TO .
137 | ASSIGN COMPONENT iv_component OF STRUCTURE TO .
138 | IF IS ASSIGNED.
139 | GET REFERENCE OF INTO lr_data.
140 | lr_res = deref( lr_data ).
141 | ENDIF.
142 | ELSE.
143 | lr_res = mr_data.
144 | ENDIF.
145 | ENDIF.
146 |
147 | IF lr_res IS NOT INITIAL AND ( iv_index IS NOT INITIAL OR iv_keys IS NOT INITIAL ).
148 | lo_type = cl_abap_typedescr=>describe_by_data_ref( lr_res ).
149 | IF lo_type->kind EQ cl_abap_typedescr=>kind_table.
150 | " check for table index access
151 | IF iv_index IS NOT INITIAL.
152 | ASSIGN lr_res->* TO .
153 | IF sy-subrc IS INITIAL.
154 | READ TABLE INDEX iv_index REFERENCE INTO lr_res.
155 | IF sy-subrc IS NOT INITIAL.
156 | CLEAR lr_res.
157 | ENDIF.
158 | ELSE.
159 | CLEAR lr_res.
160 | ENDIF.
161 | ELSE. " iv_keys IS NOT INITIAL
162 | ASSIGN lr_res->* TO .
163 | IF sy-subrc IS INITIAL.
164 | CREATE DATA lr_data LIKE LINE OF .
165 | ASSIGN lr_data->* TO .
166 | FIND ALL OCCURRENCES OF REGEX so_regex_keys IN iv_keys RESULTS lt_keys.
167 | IF sy-subrc IS INITIAL.
168 | LOOP AT lt_keys ASSIGNING .
169 | READ TABLE -submatches INDEX 1 ASSIGNING .
170 | lv_key = iv_keys+-offset(-length).
171 | TRANSLATE lv_key TO UPPER CASE.
172 | READ TABLE -submatches INDEX 2 ASSIGNING .
173 | lv_value = iv_keys+-offset(-length).
174 | ASSIGN COMPONENT lv_key OF STRUCTURE TO .
175 | CHECK sy-subrc IS INITIAL.
176 | = lv_value.
177 | ENDLOOP.
178 | ELSE.
179 | = lv_key.
180 | ENDIF.
181 | READ TABLE FROM REFERENCE INTO lr_res.
182 | IF sy-subrc IS NOT INITIAL.
183 | CLEAR lr_res.
184 | ENDIF.
185 | ELSE.
186 | CLEAR lr_res.
187 | ENDIF.
188 | ENDIF.
189 | ENDIF.
190 | ENDIF.
191 |
192 | CREATE OBJECT ro_ref
193 | EXPORTING
194 | ir_data = lr_res.
195 |
196 | ENDMETHOD. "at_int
197 |
198 |
199 | METHOD CONSTRUCTOR.
200 | IF ir_data IS NOT INITIAL.
201 | mr_data = ir_data.
202 | ELSEIF iv_data IS SUPPLIED.
203 | GET REFERENCE OF iv_data INTO mr_data.
204 | ENDIF.
205 | mr_data = deref( mr_data ).
206 | ENDMETHOD. "constructor
207 |
208 |
209 | METHOD COUNT.
210 |
211 | FIELD-SYMBOLS: TYPE ANY TABLE.
212 |
213 | IF mr_data IS BOUND.
214 | ASSIGN mr_data->* TO .
215 | IF sy-subrc IS INITIAL.
216 | rv_lines = lines( ).
217 | ENDIF.
218 | ENDIF.
219 |
220 | ENDMETHOD.
221 |
222 |
223 | METHOD CREATE.
224 | IF iv_data IS SUPPLIED.
225 | CREATE OBJECT ro_ref
226 | EXPORTING
227 | iv_data = iv_data.
228 | ELSE.
229 | CREATE OBJECT ro_ref
230 | EXPORTING
231 | ir_data = ir_data.
232 | ENDIF.
233 |
234 | IF iv_component IS NOT INITIAL.
235 | ro_ref = ro_ref->at( iv_component ).
236 | ENDIF.
237 | ENDMETHOD. "create
238 |
239 |
240 | METHOD DEREF.
241 |
242 | DATA: lo_type TYPE REF TO cl_abap_typedescr.
243 |
244 | FIELD-SYMBOLS: TYPE data.
245 |
246 | rr_data = ir_data.
247 | IF rr_data IS NOT INITIAL.
248 | lo_type = cl_abap_typedescr=>describe_by_data_ref( ir_data ).
249 | IF lo_type->kind EQ cl_abap_typedescr=>kind_ref.
250 | ASSIGN ir_data->* TO .
251 | rr_data = deref( ).
252 | ENDIF.
253 | ENDIF.
254 |
255 | ENDMETHOD. "deref
256 |
257 |
258 | METHOD EMPTY.
259 | IF mr_data IS INITIAL.
260 | rv_val = abap_true.
261 | ENDIF.
262 | ENDMETHOD. "empty
263 |
264 |
265 | METHOD REF.
266 | rv_data = mr_data.
267 | ENDMETHOD. "ref
268 |
269 |
270 | METHOD SET.
271 |
272 | FIELD-SYMBOLS: TYPE data.
273 |
274 | IF mr_data IS BOUND.
275 | ASSIGN mr_data->* TO .
276 | = iv_data.
277 | rv_success = abap_true.
278 | ENDIF.
279 |
280 | ENDMETHOD.
281 |
282 |
283 | METHOD VALUE.
284 |
285 | DATA: lo_type_out TYPE REF TO cl_abap_typedescr,
286 | lo_type_in TYPE REF TO cl_abap_typedescr.
287 |
288 | FIELD-SYMBOLS: TYPE data.
289 |
290 | CLEAR ev_data.
291 |
292 | IF mr_data IS BOUND.
293 | ASSIGN mr_data->* TO .
294 | lo_type_out = cl_abap_typedescr=>describe_by_data( ev_data ).
295 | lo_type_in = cl_abap_typedescr=>describe_by_data( ).
296 | IF lo_type_out->kind EQ lo_type_in->kind.
297 | ev_data = .
298 | ENDIF.
299 | ELSEIF iv_default IS SUPPLIED.
300 | ev_data = iv_default.
301 | ENDIF.
302 |
303 | ENDMETHOD. "value
304 |
305 |
306 | METHOD class_constructor.
307 | create_regexp so_regex_hier '(?:([\w\/]+)|^)(?:\[\s*(?:(\d+)|([^\]]+))\s*\])?(?:(?:-\>)|(?:-)|(?:=>)|$)'.
308 | create_regexp so_regex_keys '(\w+)\s*=\s*([^,]*),?'.
309 | ENDMETHOD.
310 | ENDCLASS.
311 |
--------------------------------------------------------------------------------
/src/z_ui2_data_access.clas.macros.abap:
--------------------------------------------------------------------------------
1 | *"* use this source file for any macro definitions you need
2 | *"* in the implementation part of the class
3 |
4 | DEFINE create_regexp.
5 | " first try PCRE
6 | TRY.
7 | CALL METHOD cl_abap_regex=>('CREATE_PCRE')
8 | EXPORTING
9 | pattern = &2
10 | RECEIVING
11 | regex = &1. "#EC NOTEXT
12 | CATCH cx_sy_dyn_call_error.
13 | CREATE OBJECT &1
14 | EXPORTING
15 | pattern = &2 ##REGEX_POSIX ##NO_TEXT. "#EC NOTEXT
16 | ENDTRY.
17 | END-OF-DEFINITION.
18 |
--------------------------------------------------------------------------------
/src/z_ui2_data_access.clas.testclasses.abap:
--------------------------------------------------------------------------------
1 | *"* use this source file for your ABAP unit test classes
2 | CLASS lc_ut DEFINITION FOR TESTING FINAL "#AU Duration Medium
3 | "#AU Risk_Level Harmless
4 | INHERITING FROM z_ui2_data_access.
5 |
6 | PUBLIC SECTION.
7 | TYPES:
8 | BEGIN OF t_properties,
9 | enabled TYPE abap_bool,
10 | length TYPE i,
11 | description TYPE c LENGTH 20,
12 | END OF t_properties,
13 | BEGIN OF t_data,
14 | id TYPE i,
15 | properties TYPE t_properties,
16 | content TYPE string,
17 | END OF t_data,
18 | BEGIN OF t_name_value,
19 | name TYPE string,
20 | value TYPE string,
21 | END OF t_name_value,
22 | BEGIN OF t_example,
23 | flag TYPE abap_bool,
24 | /bic/z_year TYPE i,
25 | props TYPE t_data,
26 | params TYPE SORTED TABLE OF t_name_value WITH UNIQUE KEY name,
27 | params2 TYPE SORTED TABLE OF t_name_value WITH UNIQUE KEY name value,
28 | END OF t_example.
29 |
30 | METHODS: test_basic_actions FOR TESTING.
31 | METHODS: test_table_access FOR TESTING.
32 | METHODS: test_table_access2 FOR TESTING.
33 | METHODS: test_table_access3 FOR TESTING.
34 |
35 | ENDCLASS. "lc_ut
36 |
37 |
38 | CLASS lc_ut IMPLEMENTATION.
39 |
40 | METHOD test_basic_actions.
41 |
42 | DATA: ls_data TYPE t_example,
43 | lr_data TYPE REF TO data,
44 | lr_ref TYPE REF TO data,
45 | lv_int TYPE i,
46 | lo_data TYPE REF TO z_ui2_data_access,
47 | lo_tmp LIKE lo_data.
48 |
49 | FIELD-SYMBOLS: TYPE data.
50 |
51 | ls_data-props-id = 12345.
52 | ls_data-/bic/z_year = 1978.
53 | ls_data-props-content = `Some Content`.
54 | ls_data-props-properties-enabled = abap_false.
55 | ls_data-props-properties-length = 10.
56 | ls_data-props-properties-description = `My description`.
57 |
58 | GET REFERENCE OF ls_data INTO lr_data.
59 |
60 | CREATE OBJECT lo_data EXPORTING ir_data = lr_data.
61 |
62 | " standard way (does not work on SAP_BASIS 700)
63 | lo_tmp = lo_data->at( `PROPS` ).
64 | lo_tmp = lo_tmp->at( `id` ).
65 | lr_ref = lo_tmp->ref( ).
66 | " lr_ref = lo_data->at(`PROPS`)->at(`id`)->ref( ). => 700
67 | cl_aunit_assert=>assert_bound( act = lr_ref msg = `Dynamic access to existing property fails!` ).
68 |
69 | ASSIGN lr_ref->* TO .
70 | cl_aunit_assert=>assert_equals( act = exp = ls_data-props-id msg = `Dynamic access to existing property fails!` ).
71 |
72 | lo_tmp = lo_data->at( `PROPS` ).
73 | lo_tmp = lo_tmp->at( `key` ).
74 | lr_ref = lo_tmp->ref( ).
75 | " lr_ref = lo_data->at(`PROPS`)->at(`key`)->ref( ). => 700
76 | cl_aunit_assert=>assert_not_bound( act = lr_ref msg = `Dynamic access to NOT existing property fails!` ).
77 |
78 | " XPath like
79 | lo_tmp = lo_data->at( `PROPS-ID` ).
80 | lr_ref = lo_tmp->ref( ).
81 | " lr_ref = lo_data->at(`PROPS-ID`)->ref( ). => 700
82 | cl_aunit_assert=>assert_bound( act = lr_ref msg = `Dynamic access to existing property fails!` ).
83 |
84 | ASSIGN lr_ref->* TO .
85 | cl_aunit_assert=>assert_equals( act = exp = ls_data-props-id msg = `Dynamic access to existing property fails!` ).
86 |
87 | " Alternative component separator
88 | lo_tmp = lo_data->at( `PROPS->ID` ).
89 | lr_ref = lo_tmp->ref( ).
90 | " lr_ref = lo_data->at(`PROPS->ID`)->ref( ). => 700
91 | cl_aunit_assert=>assert_bound( act = lr_ref msg = `Dynamic access to existing property fails!` ).
92 |
93 | ASSIGN lr_ref->* TO .
94 | cl_aunit_assert=>assert_equals( act = exp = ls_data-props-id msg = `Dynamic access to existing property fails!` ).
95 |
96 | " access for attributes with special characters
97 | lo_tmp = lo_data->at( `/BIC/Z_YEAR` ).
98 | lr_ref = lo_tmp->ref( ).
99 | " lr_ref = lo_data->at(`PROPS-ID`)->ref( ). => 700
100 | cl_aunit_assert=>assert_bound( act = lr_ref msg = `Dynamic access to existing property with special characters fails!` ).
101 |
102 | ASSIGN lr_ref->* TO .
103 | cl_aunit_assert=>assert_equals( act = exp = ls_data-/bic/z_year msg = `Dynamic access to existing property with special characters fails!` ).
104 |
105 | " using helper method for creation
106 | lo_tmp = create( ir_data = lr_data iv_component = `PROPS-ID` ).
107 | lr_ref = lo_tmp->ref( ).
108 | " lr_ref = create( ir_data = lr_data iv_component = `PROPS-ID`)->ref( ). => 700
109 | cl_aunit_assert=>assert_bound( act = lr_ref msg = `Dynamic access to existing property fails!` ).
110 |
111 | ASSIGN lr_ref->* TO .
112 | cl_aunit_assert=>assert_equals( act = exp = ls_data-props-id msg = `Dynamic access to existing property fails!` ).
113 |
114 | " reading value
115 | lo_tmp = lo_data->at( `props-properties-length` ).
116 | lo_tmp->value( IMPORTING ev_data = lv_int ).
117 | " lo_data->at(`props-properties-length`)->value( IMPORTING ev_data = lv_int ). => 700
118 | cl_aunit_assert=>assert_equals( act = lv_int exp = ls_data-props-properties-length msg = `Dynamic read of existing the property fails!` ).
119 |
120 | lv_int = 25.
121 | lo_tmp = lo_data->at( `props-properties-len` ).
122 | lo_tmp->value( IMPORTING ev_data = lv_int ).
123 | " lo_data->at(`props-properties-len`)->value( IMPORTING ev_data = lv_int ). => 700
124 | cl_aunit_assert=>assert_initial( act = lv_int msg = `Dynamic read of the NOT existing property fails!` ).
125 |
126 | " modifing value
127 | lv_int = 25.
128 | lo_tmp = lo_data->at( `props-properties-length` ).
129 | lr_ref = lo_tmp->ref( ).
130 | " lr_ref = lo_data->at(`props-properties-length`)->ref( ). => 700
131 | ASSIGN lr_ref->* TO .
132 | = lv_int.
133 | cl_aunit_assert=>assert_equals( act = lv_int exp = ls_data-props-properties-length msg = `Dynamic modification of the existing property fails!` ).
134 |
135 | lv_int = 15.
136 | lo_tmp = lo_data->at( `props-properties-length` ).
137 | lo_tmp->set( lv_int ).
138 | " lo_data->at(`props-properties-length`)->set( lv_int ). => 700
139 | cl_aunit_assert=>assert_equals( act = lv_int exp = ls_data-props-properties-length msg = `Dynamic modification of the existing property fails!` ).
140 |
141 | " degenerated example
142 | lv_int = 15.
143 | lo_tmp = create( iv_data = lv_int ).
144 | lr_ref = lo_tmp->ref( ).
145 | " lr_ref = create( iv_data = lv_int )->ref( ). => 700
146 | ASSIGN lr_ref->* TO .
147 | cl_aunit_assert=>assert_equals( act = exp = lv_int msg = `Dynamic access of existing the property fails!` ).
148 |
149 | ENDMETHOD.
150 |
151 | METHOD test_table_access.
152 |
153 | DATA:
154 | ls_data TYPE t_example,
155 | ls_line LIKE LINE OF ls_data-params,
156 | lv_value TYPE string,
157 | lo_data TYPE REF TO z_ui2_data_access.
158 |
159 | ls_line-name = `KEY1`.
160 | ls_line-value = `Value1`.
161 | INSERT ls_line INTO TABLE ls_data-params.
162 | INSERT ls_line INTO TABLE ls_data-params2.
163 |
164 | ls_line-name = `KEY2`.
165 | ls_line-value = `Value1`.
166 | INSERT ls_line INTO TABLE ls_data-params.
167 | INSERT ls_line INTO TABLE ls_data-params2.
168 |
169 | ls_line-name = `KEY2`.
170 | ls_line-value = `Value2`.
171 | INSERT ls_line INTO TABLE ls_data-params.
172 | INSERT ls_line INTO TABLE ls_data-params2.
173 |
174 | ls_line-name = `KEY3`.
175 | ls_line-value = `Value1, Value2`.
176 | INSERT ls_line INTO TABLE ls_data-params.
177 | INSERT ls_line INTO TABLE ls_data-params2.
178 |
179 | lo_data = create( iv_data = ls_data iv_component = `params[2]-name` ).
180 | lo_data->value( IMPORTING ev_data = lv_value ).
181 |
182 | cl_aunit_assert=>assert_equals( act = lv_value exp = `KEY2` msg = `Dynamic read to table item by index fails!` ).
183 |
184 | lo_data = create( iv_data = ls_data iv_component = `params[name=KEY1]-value` ).
185 | lo_data->value( IMPORTING ev_data = lv_value ).
186 |
187 | cl_aunit_assert=>assert_equals( act = lv_value exp = `Value1` msg = `Dynamic read to table item by key index fails!` ).
188 |
189 | lo_data = create( iv_data = ls_data iv_component = `params` ).
190 | lo_data = lo_data->at( `[name=KEY1]-value` ).
191 | lo_data->value( IMPORTING ev_data = lv_value ).
192 |
193 | cl_aunit_assert=>assert_equals( act = lv_value exp = `Value1` msg = `Dynamic read to table item by key index fails!` ).
194 |
195 | lo_data = create( iv_data = ls_data iv_component = `params2[name=KEY2, value=Value2]-value` ).
196 | lo_data->value( IMPORTING ev_data = lv_value ).
197 |
198 | cl_aunit_assert=>assert_equals( act = lv_value exp = `Value2` msg = `Dynamic read to table item by key index fails!` ).
199 |
200 | " we do not support usage usage of "," or "]" as part of the key :(
201 | lo_data = create( iv_data = ls_data iv_component = `params2[name=KEY3, value=Value1, Value2]-value` ).
202 | lo_data->value( IMPORTING ev_data = lv_value ).
203 |
204 | cl_aunit_assert=>assert_equals( act = lv_value exp = `` msg = `Dynamic read to table item by unescaped key works!` ).
205 |
206 | ENDMETHOD.
207 |
208 | METHOD test_table_access2.
209 |
210 | DATA: lr_data TYPE REF TO data,
211 | lr_ref TYPE REF TO data,
212 | lo_data TYPE REF TO z_ui2_data_access,
213 | lv_json TYPE z_ui2_json=>json.
214 |
215 | lv_json = `{"type": "UPDATE","organization": {"duns": "429773946"},"elements": [{"element": "organization","current": null,"timestamp": "2019-05-01T19:56:20Z"}]}`.
216 | lr_data = z_ui2_json=>generate( json = lv_json ).
217 | lo_data = create( ir_data = lr_data iv_component = 'elements[1]-current[1]-value' ).
218 | lr_ref = lo_data->ref( ).
219 |
220 | cl_aunit_assert=>assert_not_bound( act = lr_ref msg = `Access to null array fails!` ).
221 |
222 | ENDMETHOD.
223 |
224 | METHOD test_table_access3.
225 |
226 | DATA: access TYPE REF TO z_ui2_data_access,
227 | itab TYPE REF TO z_ui2_data_access,
228 | value TYPE REF TO z_ui2_data_access,
229 | lr_data TYPE REF TO data,
230 | index_act TYPE string,
231 | index_exp TYPE string,
232 | lv_key TYPE string,
233 | json TYPE z_ui2_json=>json.
234 |
235 | CONCATENATE
236 | `{ "SSDC": {`
237 | `"checks_for_new_checkset": [`
238 | `{ "check_name": "OLC_1", "sort_order": "1", "mandatory": "X" },`
239 | `{ "check_name": "OLC_2", "sort_order": "2", "mandatory": " " },`
240 | `{ "check_name": "OLC_3", "sort_order": "3", "mandatory": "X" } ] } }`
241 | INTO json.
242 |
243 | lr_data = z_ui2_json=>generate( json = json ).
244 | access = create( ir_data = lr_data ).
245 | itab = access->at( `SSDC-checks_for_new_checkset` ).
246 |
247 | DO 3 TIMES.
248 | index_exp = sy-index.
249 | CONDENSE index_exp.
250 | CONCATENATE '[' index_exp ']-sort_order' INTO lv_key.
251 | value = itab->at( lv_key ).
252 | value->value( IMPORTING ev_data = index_act ).
253 | cl_aunit_assert=>assert_equals( act = index_act exp = index_exp msg = `Access with index fails!` ).
254 | ENDDO.
255 |
256 | ENDMETHOD.
257 |
258 | ENDCLASS.
259 |
--------------------------------------------------------------------------------
/src/z_ui2_data_access.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Z_UI2_DATA_ACCESS
7 | E
8 | Dynamic Data Access Helper Class
9 | 1
10 | X
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | AT
18 | E
19 | Component accessor
20 |
21 |
22 | AT_INT
23 | E
24 | Internal helper for comonent access
25 |
26 |
27 | CLASS_CONSTRUCTOR
28 | E
29 | CLASS_CONSTRUCTOR
30 |
31 |
32 | CONSTRUCTOR
33 | E
34 | Constructor
35 |
36 |
37 | COUNT
38 | E
39 | Return number of table lines, if attached object is table
40 |
41 |
42 | CREATE
43 | E
44 | Helper method for creating instance of dynamic accessor
45 |
46 |
47 | DEREF
48 | E
49 | Dereference deep nested references
50 |
51 |
52 | EMPTY
53 | E
54 | Returns TRUE if embedded object is initial (not bound)
55 |
56 |
57 | MR_DATA
58 | E
59 | Reference to the data
60 |
61 |
62 | REF
63 | E
64 | Returns reference to embedded object
65 |
66 |
67 | SET
68 | E
69 | Sets value of the embedded object, if not initial
70 |
71 |
72 | SO_REGEX_HIER
73 | E
74 | Cached regexp for splitting pathes
75 |
76 |
77 | SO_REGEX_KEYS
78 | E
79 | Cached regexp for extracting keys
80 |
81 |
82 | VALUE
83 | E
84 | Returns copy of the value
85 |
86 |
87 |
88 |
89 | AT
90 | IV_COMPONENT
91 | E
92 | Sub-component name (xpath is supported)
93 |
94 |
95 | AT
96 | RO_REF
97 | E
98 | Reference to accessor object pointing to subcomponent
99 |
100 |
101 | CONSTRUCTOR
102 | IR_DATA
103 | E
104 | Reference to data (allows modification)
105 |
106 |
107 | CONSTRUCTOR
108 | IV_DATA
109 | E
110 | Data (modification not allowed)
111 |
112 |
113 | COUNT
114 | RV_LINES
115 | E
116 | Lines count
117 |
118 |
119 | CREATE
120 | IR_DATA
121 | E
122 | Reference to data (allows modification)
123 |
124 |
125 | CREATE
126 | IV_COMPONENT
127 | E
128 | Sub-component name (xpath is supported)
129 |
130 |
131 | CREATE
132 | IV_DATA
133 | E
134 | Data (modification not allowed)
135 |
136 |
137 | CREATE
138 | RO_REF
139 | E
140 | Reference to accessor object pointing to subcomponent
141 |
142 |
143 | EMPTY
144 | RV_VAL
145 | E
146 | ABAP_TRUE if object is initial and data is not bound
147 |
148 |
149 | REF
150 | RV_DATA
151 | E
152 | Reference to embedded data
153 |
154 |
155 | SET
156 | IV_DATA
157 | E
158 | New value for embedded object
159 |
160 |
161 | SET
162 | RV_SUCCESS
163 | E
164 | TRUE, if data was successfully modified
165 |
166 |
167 | VALUE
168 | EV_DATA
169 | E
170 | Copy of the embedded data value, or initial
171 |
172 |
173 | VALUE
174 | IV_DEFAULT
175 | E
176 | Default value to be returned, if no data is assigned
177 |
178 |
179 |
180 |
181 |
182 |
--------------------------------------------------------------------------------
/src/z_ui2_json.clas.locals_imp.abap:
--------------------------------------------------------------------------------
1 | *"* local class implementation for public class
2 | *"* use this source file for the implementation part of
3 | *"* local helper classes
4 |
5 | *----------------------------------------------------------------------*
6 | * CLASS lcl_util DEFINITION
7 | *----------------------------------------------------------------------*
8 | *
9 | *----------------------------------------------------------------------*
10 |
11 | CLASS lcl_util DEFINITION FINAL FRIENDS z_ui2_json.
12 |
13 | PUBLIC SECTION.
14 | CLASS-METHODS:
15 | class_constructor.
16 |
17 | PRIVATE SECTION.
18 | CLASS-DATA:
19 | so_regex_iso8601 TYPE REF TO cl_abap_regex,
20 | so_regex_edm_date_time TYPE REF TO cl_abap_regex.
21 |
22 | CLASS-METHODS:
23 | _escape IMPORTING in TYPE data EXPORTING out TYPE string,
24 | to_md5 IMPORTING iv_value TYPE string RETURNING VALUE(rv_result) TYPE string,
25 | read_string IMPORTING json TYPE string mark TYPE i CHANGING offset TYPE i DEFAULT 0 text TYPE string RAISING cx_sy_move_cast_error,
26 | read_iso8601 IMPORTING in TYPE string RETURNING VALUE(rv_tstm) TYPE timestampl,
27 | read_edm_datetime IMPORTING in TYPE string RETURNING VALUE(rv_tstm) TYPE timestampl,
28 | describe_type IMPORTING io_type_descr TYPE REF TO cl_abap_typedescr RETURNING VALUE(rv_typename) TYPE string.
29 |
30 | ENDCLASS. "lcl_util DEFINITION
31 |
32 | *----------------------------------------------------------------------*
33 | * CLASS lcl_util IMPLEMENTATION
34 | *----------------------------------------------------------------------*
35 | *
36 | *----------------------------------------------------------------------*
37 | CLASS lcl_util IMPLEMENTATION.
38 |
39 | METHOD class_constructor.
40 |
41 | " support for ISO8601 => https://en.wikipedia.org/wiki/ISO_8601
42 | create_regexp so_regex_iso8601 '^(?:(\d{4})-?(\d{2})-?(\d{2}))?(?:T(\d{2}):?(\d{2})(?::?(\d{2}))?(?:[\.,](\d{0,9}))?(?:Z|(?:([+-])(\d{2})(?::?(\d{2}))?))?)?\s*$'.
43 | " support for Edm.DateTime => http://www.odata.org/documentation/odata-version-2-0/json-format/
44 | create_regexp so_regex_edm_date_time '^\/[Dd][Aa][Tt][Ee]\((-?\d+)(?:([+-])(\d{1,4}))?\)\/\s*$'.
45 |
46 | ENDMETHOD.
47 |
48 | METHOD describe_type.
49 |
50 | DATA: lv_kind_name TYPE string,
51 | lv_pos TYPE i.
52 |
53 | rv_typename = `?`.
54 |
55 | CHECK io_type_descr IS NOT INITIAL.
56 |
57 | FIND FIRST OCCURRENCE OF '\TYPE=' IN io_type_descr->absolute_name MATCH OFFSET lv_pos.
58 | IF sy-subrc IS INITIAL.
59 | lv_pos = lv_pos + 6.
60 | IF io_type_descr->absolute_name+lv_pos(1) NE '%'.
61 | rv_typename = io_type_descr->absolute_name+lv_pos.
62 | ELSE.
63 | CLEAR rv_typename.
64 | ENDIF.
65 | ELSE.
66 | rv_typename = io_type_descr->absolute_name.
67 | ENDIF.
68 |
69 | CASE io_type_descr->kind.
70 | WHEN cl_abap_typedescr=>kind_table.
71 | lv_kind_name = `TABLE`.
72 | WHEN cl_abap_typedescr=>kind_struct.
73 | lv_kind_name = `STRUCTURE`.
74 | WHEN cl_abap_typedescr=>kind_class.
75 | lv_kind_name = `CLASS`.
76 | WHEN cl_abap_typedescr=>kind_intf.
77 | lv_kind_name = `INTERFACE`.
78 | WHEN cl_abap_typedescr=>kind_ref.
79 | lv_kind_name = `REFERENCE`.
80 | ENDCASE.
81 |
82 | IF lv_kind_name IS NOT INITIAL.
83 | IF rv_typename IS NOT INITIAL.
84 | CONCATENATE lv_kind_name `(` rv_typename `)` INTO rv_typename.
85 | ELSE.
86 | rv_typename = lv_kind_name.
87 | ENDIF.
88 | ENDIF.
89 |
90 | ENDMETHOD.
91 |
92 | METHOD read_string.
93 |
94 | DATA: match LIKE offset,
95 | pos LIKE offset.
96 |
97 | " ASSERT json+offset(1) EQ `"`.
98 |
99 | DO.
100 | FIND FIRST OCCURRENCE OF `"` IN SECTION OFFSET offset OF json MATCH OFFSET pos.
101 | IF sy-subrc IS NOT INITIAL.
102 | throw_error.
103 | ENDIF.
104 |
105 | offset = pos.
106 | pos = pos - 1.
107 | " if escaped search further
108 | WHILE json+pos(1) EQ `\`.
109 | pos = pos - 1.
110 | ENDWHILE.
111 | match = ( offset - pos ) MOD 2.
112 | IF match NE 0.
113 | EXIT.
114 | ENDIF.
115 | offset = offset + 1.
116 | ENDDO.
117 |
118 | match = offset - mark.
119 | text = json+mark(match).
120 |
121 | ENDMETHOD. "read_string
122 |
123 | METHOD read_iso8601.
124 |
125 | DATA: offset_sign TYPE c,
126 | offset_hours TYPE c LENGTH 2,
127 | offset_minutes TYPE c LENGTH 2,
128 | stimestmp TYPE c LENGTH 22,
129 | seconds TYPE i.
130 |
131 | FIND FIRST OCCURRENCE OF REGEX so_regex_iso8601 IN in SUBMATCHES stimestmp stimestmp+4 stimestmp+6 stimestmp+8 stimestmp+10 stimestmp+12 stimestmp+15 offset_sign offset_hours offset_minutes.
132 | CHECK sy-subrc IS INITIAL.
133 |
134 | IF stimestmp+15(1) IS NOT INITIAL. " msec provided
135 | stimestmp+14(1) = '.'.
136 | ELSEIF stimestmp+8(1) IS INITIAL. " date-only, default to 000000 time
137 | stimestmp+8(6) = '000000'.
138 | ELSEIF stimestmp(1) IS INITIAL. " time-only, default to current date
139 | stimestmp(8) = sy-datlo.
140 | ENDIF.
141 |
142 | rv_tstm = stimestmp.
143 |
144 | IF offset_sign IS NOT INITIAL.
145 | seconds = offset_hours * 3600 + offset_minutes * 60.
146 | IF offset_sign EQ '+'.
147 | rv_tstm = cl_abap_tstmp=>subtractsecs( tstmp = rv_tstm secs = seconds ).
148 | ELSE.
149 | rv_tstm = cl_abap_tstmp=>add( tstmp = rv_tstm secs = seconds ).
150 | ENDIF.
151 | ENDIF.
152 |
153 | ENDMETHOD.
154 |
155 | METHOD read_edm_datetime.
156 |
157 | CONSTANTS: lc_epochs TYPE c LENGTH 15 VALUE '19700101000000.'.
158 |
159 | DATA: ticks TYPE c LENGTH 21,
160 | offset_sign TYPE c,
161 | offset TYPE c LENGTH 4,
162 | pticks TYPE p,
163 | pseconds TYPE p,
164 | psubsec TYPE p,
165 | stimestmp TYPE string.
166 |
167 | FIND FIRST OCCURRENCE OF REGEX so_regex_edm_date_time IN in SUBMATCHES ticks offset_sign offset.
168 | CHECK sy-subrc IS INITIAL.
169 |
170 | pticks = ticks.
171 | pseconds = pticks / 1000. " in seconds
172 | psubsec = pticks MOD 1000. " in subsec
173 |
174 | stimestmp = psubsec.
175 | CONCATENATE lc_epochs stimestmp INTO stimestmp.
176 | rv_tstm = stimestmp.
177 |
178 | rv_tstm = cl_abap_tstmp=>add( tstmp = rv_tstm secs = pseconds ).
179 |
180 | IF offset_sign IS NOT INITIAL.
181 | pticks = offset * 60. "offset is in minutes
182 | IF offset_sign EQ '+'.
183 | rv_tstm = cl_abap_tstmp=>subtractsecs( tstmp = rv_tstm secs = pticks ).
184 | ELSE.
185 | rv_tstm = cl_abap_tstmp=>add( tstmp = rv_tstm secs = pticks ).
186 | ENDIF.
187 | ENDIF.
188 |
189 | ENDMETHOD.
190 |
191 | " Creates MD5 hash from string
192 | METHOD to_md5.
193 | DATA: lv_md5_key TYPE hash160.
194 |
195 | IF iv_value IS NOT INITIAL.
196 | CALL FUNCTION 'CALCULATE_HASH_FOR_CHAR'
197 | EXPORTING
198 | alg = 'MD5'
199 | data = iv_value
200 | IMPORTING
201 | hash = lv_md5_key
202 | EXCEPTIONS
203 | OTHERS = 4.
204 |
205 | IF sy-subrc EQ 0.
206 | rv_result = lv_md5_key.
207 | ENDIF.
208 | ENDIF.
209 | ENDMETHOD. "to_md5
210 |
211 | METHOD _escape.
212 | out = escape( val = in format = cl_abap_format=>e_json_string ).
213 | ENDMETHOD. "_escape
214 |
215 | ENDCLASS. "lcl_util IMPLEMENTATION
216 |
217 | *----------------------------------------------------------------------*
218 | * CLASS lcl_test DEFINITION
219 | *----------------------------------------------------------------------*
220 | *
221 | *----------------------------------------------------------------------*
222 | CLASS lcl_test DEFINITION FINAL FRIENDS z_ui2_json.
223 |
224 | PUBLIC SECTION.
225 | DATA: id TYPE i.
226 | DATA: children TYPE STANDARD TABLE OF REF TO lcl_test.
227 |
228 | METHODS: constructor.
229 |
230 | PROTECTED SECTION. "#EC SEC_PROTEC
231 | DATA: prot TYPE i. "#EC NEEDED
232 |
233 | PRIVATE SECTION.
234 | DATA: priv TYPE i. "#EC NEEDED
235 |
236 | ENDCLASS. "lcl_test DEFINITION
237 |
238 | *----------------------------------------------------------------------*
239 | * CLASS lcl_test IMPLEMENTATION
240 | *----------------------------------------------------------------------*
241 | *
242 | *----------------------------------------------------------------------*
243 | CLASS lcl_test IMPLEMENTATION.
244 |
245 | METHOD constructor.
246 | priv = 1.
247 | prot = 2.
248 | ENDMETHOD. "constructor
249 |
250 | ENDCLASS. "lcl_test IMPLEMENTATION
251 |
252 | *----------------------------------------------------------------------*
253 | * CLASS lc_json_custom DEFINITION
254 | *----------------------------------------------------------------------*
255 | *
256 | *----------------------------------------------------------------------*
257 | CLASS lc_json_custom DEFINITION FINAL INHERITING FROM z_ui2_json.
258 | PUBLIC SECTION.
259 | CLASS-METHODS:
260 | serialize_ex IMPORTING data TYPE data
261 | compress TYPE bool DEFAULT c_bool-false
262 | pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none
263 | RETURNING VALUE(r_json) TYPE json,
264 | deserialize_ex IMPORTING json TYPE json OPTIONAL
265 | pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none
266 | CHANGING data TYPE data.
267 |
268 | PROTECTED SECTION.
269 | METHODS:
270 | is_compressable REDEFINITION,
271 | pretty_name_ex REDEFINITION,
272 | dump_type REDEFINITION.
273 | ENDCLASS. "lc_json_custom DEFINITION
274 |
275 | *----------------------------------------------------------------------*
276 | * CLASS lc_json_custom IMPLEMENTATION
277 | *----------------------------------------------------------------------*
278 | *
279 | *----------------------------------------------------------------------*
280 | CLASS lc_json_custom IMPLEMENTATION.
281 |
282 | METHOD serialize_ex.
283 | DATA: lo_json TYPE REF TO lc_json_custom.
284 | CREATE OBJECT lo_json
285 | EXPORTING
286 | compress = compress
287 | pretty_name = pretty_name
288 | assoc_arrays = abap_true
289 | assoc_arrays_opt = abap_true
290 | expand_includes = abap_true
291 | numc_as_string = abap_true
292 | bool_types = `\TYPE-POOL=ABAP\TYPE=ABAP_BOOL\TYPE=BOOLEAN\TYPE=/UI2/BOOLEAN`
293 | ts_as_iso8601 = abap_true.
294 | r_json = lo_json->serialize_int( data = data ).
295 | ENDMETHOD. "serialize_ex
296 |
297 | METHOD deserialize_ex.
298 | DATA: lo_json TYPE REF TO lc_json_custom.
299 | IF json IS NOT INITIAL.
300 | CREATE OBJECT lo_json
301 | EXPORTING
302 | pretty_name = pretty_name
303 | assoc_arrays = abap_true
304 | assoc_arrays_opt = abap_true.
305 | TRY .
306 | lo_json->deserialize_int( EXPORTING json = json CHANGING data = data ).
307 | CATCH cx_sy_move_cast_error. "#EC NO_HANDLER
308 | ENDTRY.
309 | ENDIF.
310 | ENDMETHOD. "deserialize_ex
311 |
312 | METHOD is_compressable.
313 | IF type_descr->absolute_name EQ `\TYPE=STRING` OR name EQ `INITIAL`.
314 | rv_compress = abap_false.
315 | ELSE.
316 | rv_compress = abap_true.
317 | ENDIF.
318 | ENDMETHOD. "is_compressable
319 |
320 | METHOD pretty_name_ex.
321 | out = super->pretty_name_ex( in ).
322 | CONCATENATE out 'Xxx' INTO out.
323 | ENDMETHOD. "pretty_name
324 |
325 | METHOD dump_type.
326 |
327 | DATA: is_ddic TYPE abap_bool,
328 | ddic_field TYPE dfies.
329 |
330 | is_ddic = type_descr->is_ddic_type( ).
331 | IF is_ddic EQ abap_true.
332 | ddic_field = type_descr->get_ddic_field( ).
333 | IF mv_ts_as_iso8601 EQ c_bool-true AND ddic_field-domname EQ `TZNTSTMPL`.
334 | r_json = data.
335 | CONCATENATE `"` r_json(4) `-` r_json+4(2) `-` r_json+6(2) `T` r_json+8(2) `:` r_json+10(2) `:` r_json+12(2) `.` r_json+15(7) `Z"` INTO r_json.
336 | RETURN.
337 | ENDIF.
338 | ENDIF.
339 | IF mv_ts_as_iso8601 EQ c_bool-true AND type_descr->absolute_name EQ `\TYPE=LCM_CHANGED_ON`.
340 | r_json = data.
341 | CONCATENATE `"` r_json(4) `-` r_json+4(2) `-` r_json+6(2) `T` r_json+8(2) `:` r_json+10(2) `:` r_json+12(2) `.` r_json+15(7) `Z"` INTO r_json.
342 | RETURN.
343 | ENDIF.
344 |
345 | r_json = super->dump_type( data = data type_descr = type_descr convexit = convexit ).
346 |
347 | ENDMETHOD. "dump_type
348 |
349 | ENDCLASS. "lc_json_custom
350 |
--------------------------------------------------------------------------------
/src/z_ui2_json.clas.macros.abap:
--------------------------------------------------------------------------------
1 | *"* use this source file for any macro definitions you need
2 | *"* in the implementation part of the class
3 |
4 | DEFINE escape_json.
5 | " => 7.31
6 | * &2 = escape( val = &1 format = cl_abap_format=>e_json_string ).
7 |
8 | lcl_util=>_escape( EXPORTING in = &1 IMPORTING out = &2 ).
9 |
10 | * <= 7.31 (not full scope of escaping is supported)
11 | * &2 = &1.
12 | *
13 | ** replace all occurrences of regex `[\\"]` in &1 with `\\$0`. <-- this is slower than 2 plain replaces
14 | * REPLACE ALL OCCURRENCES OF '\' IN &2 WITH '\\'.
15 | * REPLACE ALL OCCURRENCES OF '"' IN &2 WITH '\"'.
16 | *
17 | * REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN &2 WITH '\r\n'.
18 | * REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline IN &2 WITH '\n'.
19 | * REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>horizontal_tab IN &2 WITH '\t'.
20 | END-OF-DEFINITION.
21 |
22 | DEFINE is_compressable.
23 | IF mv_compress EQ abap_false.
24 | &3 = abap_false.
25 | ELSEIF mv_extended IS INITIAL.
26 | &3 = abap_true.
27 | ELSE.
28 | &3 = is_compressable( type_descr = &1 name = &2 ).
29 | ENDIF.
30 | END-OF-DEFINITION.
31 |
32 | DEFINE dump_type.
33 | IF mv_extended IS INITIAL.
34 | dump_type_int &1 &3 &4 &5.
35 | ELSE.
36 | &4 = dump_type( data = &1 type_descr = &2 typekind = &3 convexit = &5 ).
37 | ENDIF.
38 | END-OF-DEFINITION.
39 |
40 | DEFINE xstring_to_string_int.
41 | IF mv_hex_as_base64 IS INITIAL.
42 | MOVE &1 TO &2.
43 | ELSE.
44 | &2 = xstring_to_string( &1 ).
45 | ENDIF.
46 | END-OF-DEFINITION.
47 |
48 | DEFINE string_to_xstring_int.
49 | IF mv_hex_as_base64 IS INITIAL.
50 | MOVE &1 TO &2.
51 | ELSE.
52 | string_to_xstring( EXPORTING in = &1 CHANGING out = &2 ).
53 | ENDIF.
54 | END-OF-DEFINITION.
55 |
56 | DEFINE format_list_output.
57 | IF mv_format_output EQ abap_true AND &2 IS NOT INITIAL.
58 | CONCATENATE ',' lv_indent INTO lv_lb.
59 | CONCATENATE LINES OF &2 INTO &4 SEPARATED BY lv_lb.
60 | CONCATENATE &1 lv_indent &4 indent &3 INTO &4.
61 | ELSE.
62 | CONCATENATE LINES OF &2 INTO &4 SEPARATED BY ','.
63 | CONCATENATE &1 &4 &3 INTO &4.
64 | ENDIF.
65 | END-OF-DEFINITION. " format_list_output
66 |
67 | DEFINE dump_type_int.
68 |
69 | CASE &2.
70 | WHEN e_typekind-convexit.
71 | IF &1 IS INITIAL.
72 | &3 = `""`.
73 | ELSE.
74 | TRY.
75 | CALL FUNCTION &4
76 | EXPORTING
77 | input = &1
78 | IMPORTING
79 | output = &3
80 | EXCEPTIONS
81 | OTHERS = 1.
82 | IF sy-subrc IS INITIAL.
83 | CONCATENATE '"' &3 '"' INTO &3.
84 | ENDIF.
85 | CATCH cx_root ##CATCH_ALL ##NO_HANDLER.
86 | ENDTRY.
87 | ENDIF.
88 | WHEN e_typekind-utclong.
89 | IF &1 IS INITIAL.
90 | &3 = mv_initial_ts.
91 | ELSE.
92 | DATA: utcl TYPE c LENGTH 27.
93 | utcl = &1.
94 | CONCATENATE '"' utcl(10) 'T' utcl+11(16) 'Z"' INTO &3.
95 | ENDIF.
96 | WHEN e_typekind-ts_iso8601.
97 | IF &1 IS INITIAL.
98 | &3 = mv_initial_ts.
99 | ELSE.
100 | DATA: ts TYPE c LENGTH 14.
101 | ts = &1.
102 | CONCATENATE '"' ts(4) '-' ts+4(2) '-' ts+6(2) 'T' ts+8(2) ':' ts+10(2) ':' ts+12(2) 'Z"' INTO &3.
103 | ENDIF.
104 | WHEN e_typekind-tsl_iso8601.
105 | IF &1 IS INITIAL.
106 | &3 = mv_initial_ts.
107 | ELSE.
108 | DATA: tsl TYPE c LENGTH 22.
109 | tsl = &1.
110 | CONCATENATE '"' tsl(4) '-' tsl+4(2) '-' tsl+6(2) 'T' tsl+8(2) ':' tsl+10(2) ':' tsl+12(2) '.' tsl+15(7) 'Z"' INTO &3.
111 | ENDIF.
112 | WHEN e_typekind-float.
113 | IF &1 IS INITIAL.
114 | &3 = `0`.
115 | ELSE.
116 | &3 = &1.
117 | ENDIF.
118 | WHEN e_typekind-int OR e_typekind-int1 OR e_typekind-int2 OR e_typekind-packed OR e_typekind-int8.
119 | IF &1 IS INITIAL.
120 | &3 = `0`.
121 | ELSE.
122 | &3 = &1.
123 | IF &1 LT 0.
124 | SHIFT &3 RIGHT CIRCULAR.
125 | ELSE.
126 | CONDENSE &3.
127 | ENDIF.
128 | ENDIF.
129 | WHEN e_typekind-numc_string.
130 | IF &1 IS INITIAL.
131 | &3 = `""`.
132 | ELSE.
133 | CONCATENATE '"' &1 '"' INTO &3.
134 | ENDIF.
135 | WHEN e_typekind-num.
136 | IF &1 IS INITIAL.
137 | &3 = `0`.
138 | ELSE.
139 | &3 = &1.
140 | SHIFT &3 LEFT DELETING LEADING '0'.
141 | ENDIF.
142 | WHEN e_typekind-json.
143 | &3 = &1.
144 | WHEN e_typekind-string OR e_typekind-csequence OR e_typekind-clike OR e_typekind-char.
145 | IF &1 IS INITIAL.
146 | &3 = `""`.
147 | ELSE.
148 | escape_json &1 &3.
149 | CONCATENATE '"' &3 '"' INTO &3.
150 | ENDIF.
151 | WHEN cl_abap_typedescr=>typekind_xstring OR cl_abap_typedescr=>typekind_hex.
152 | IF &1 IS INITIAL.
153 | &3 = `""`.
154 | ELSE.
155 | xstring_to_string_int &1 &3.
156 | CONCATENATE '"' &3 '"' INTO &3.
157 | ENDIF.
158 | WHEN e_typekind-bool OR e_typekind-tribool.
159 | IF &1 EQ c_bool-true.
160 | &3 = `true`. "#EC NOTEXT
161 | ELSEIF &1 IS INITIAL AND &2 EQ e_typekind-tribool.
162 | &3 = `null`. "#EC NOTEXT
163 | ELSE.
164 | &3 = `false`. "#EC NOTEXT
165 | ENDIF.
166 | WHEN e_typekind-date.
167 | IF &1 IS INITIAL.
168 | &3 = mv_initial_date.
169 | ELSE.
170 | CONCATENATE '"' &1(4) '-' &1+4(2) '-' &1+6(2) '"' INTO &3.
171 | ENDIF.
172 | WHEN e_typekind-time.
173 | IF &1 IS INITIAL.
174 | &3 = mv_initial_time.
175 | ELSE.
176 | CONCATENATE '"' &1(2) ':' &1+2(2) ':' &1+4(2) '"' INTO &3.
177 | ENDIF.
178 | WHEN e_typekind-enum.
179 | &3 = &1.
180 | CONCATENATE '"' &3 '"' INTO &3.
181 | WHEN OTHERS.
182 | IF &1 IS INITIAL.
183 | &3 = `null`. "#EC NOTEXT
184 | ELSE.
185 | &3 = &1.
186 | ENDIF.
187 | ENDCASE.
188 |
189 | END-OF-DEFINITION.
190 |
191 | DEFINE format_name.
192 | CASE &2.
193 | WHEN pretty_mode-camel_case.
194 | &3 = pretty_name( &1 ).
195 | WHEN pretty_mode-pascal_case.
196 | &3 = pretty_name( in = &1 pascal_case = c_bool-true ).
197 | WHEN pretty_mode-extended.
198 | &3 = pretty_name_ex( &1 ).
199 | WHEN pretty_mode-user_low_case.
200 | READ TABLE mt_name_mappings WITH TABLE KEY abap = &1 ASSIGNING . "#EC WARNOK
201 | IF sy-subrc IS INITIAL.
202 | &3 = -json.
203 | ELSE.
204 | &3 = &1.
205 | TRANSLATE &3 TO LOWER CASE.
206 | ENDIF.
207 | WHEN pretty_mode-user.
208 | READ TABLE mt_name_mappings WITH TABLE KEY abap = &1 ASSIGNING . "#EC WARNOK
209 | IF sy-subrc IS INITIAL.
210 | &3 = -json.
211 | ELSE.
212 | &3 = &1.
213 | ENDIF.
214 | WHEN pretty_mode-low_case.
215 | &3 = &1.
216 | TRANSLATE &3 TO LOWER CASE.
217 | WHEN OTHERS.
218 | &3 = &1.
219 | ENDCASE.
220 | END-OF-DEFINITION.
221 |
222 | DEFINE restore_reference.
223 | CREATE DATA data TYPE HANDLE &1.
224 | ASSIGN data->* TO .
225 | restore_type( EXPORTING json = json length = length type_descr = &1 CHANGING offset = offset data = ).
226 | END-OF-DEFINITION.
227 |
228 | DEFINE restore_reference_ex. " &1 - data, &2 - type_descr
229 | ref_descr ?= &2.
230 | data_descr ?= ref_descr->get_referenced_type( ).
231 | IF &1 IS INITIAL.
232 | IF data_descr->type_kind EQ data_descr->typekind_data. " REF TO DATA
233 | generate_int_ex( EXPORTING json = json length = length CHANGING offset = offset data = &1 ).
234 | RETURN.
235 | ELSE.
236 | CREATE DATA &1 TYPE HANDLE data_descr.
237 | ENDIF.
238 | ENDIF.
239 | data_ref ?= &1.
240 | ASSIGN data_ref->* TO .
241 | restore_type( EXPORTING json = json length = length type_descr = data_descr CHANGING data = offset = offset ).
242 | END-OF-DEFINITION.
243 |
244 | DEFINE throw_error.
245 | " RAISE EXCEPTION TYPE cx_sy_move_cast_error EXPORTING source_typename = |({ offset })|.
246 | RAISE EXCEPTION TYPE cx_sy_move_cast_error.
247 | END-OF-DEFINITION.
248 |
249 | DEFINE while_offset_cs.
250 |
251 | * >= 7.02 alternative
252 | &2 = find_any_not_of( val = json sub = &1 off = &2 ) ##NO_TEXT.
253 | IF &2 EQ -1.
254 | &2 = length.
255 | ENDIF.
256 | * >= 7.02
257 |
258 | * < 7.02
259 | * WHILE &2 < length.
260 | * FIND FIRST OCCURRENCE OF json+&2(1) IN &1.
261 | * IF sy-subrc IS NOT INITIAL.
262 | * EXIT.
263 | * ENDIF.
264 | * &2 = &2 + 1.
265 | * ENDWHILE.
266 | * < 7.02
267 |
268 | END-OF-DEFINITION.
269 |
270 | DEFINE while_offset_not_cs.
271 |
272 | * >= 7.02 alternative
273 | &3 = find_any_of( val = &2 sub = &1 off = &3 ).
274 | IF &3 EQ -1.
275 | &3 = length.
276 | ENDIF.
277 | * >= 7.02
278 |
279 | * < 7.02
280 | * WHILE &3 < length.
281 | * FIND FIRST OCCURRENCE OF &2+&3(1) IN &1.
282 | * IF sy-subrc IS INITIAL.
283 | * EXIT.
284 | * ENDIF.
285 | * &3 = &3 + 1.
286 | * ENDWHILE.
287 | * < 7.02
288 |
289 | END-OF-DEFINITION.
290 |
291 | DEFINE eat_white.
292 | while_offset_cs sv_white_space offset.
293 | IF offset GE length.
294 | throw_error.
295 | ENDIF.
296 | END-OF-DEFINITION.
297 |
298 | DEFINE eat_name.
299 |
300 | IF json+offset(1) NE '"'.
301 | throw_error.
302 | ENDIF.
303 |
304 | offset = mark = offset + 1.
305 | IF mark < length AND json+mark(1) EQ '"'.
306 | CLEAR &1.
307 | ELSE.
308 | FIND FIRST OCCURRENCE OF '"' IN SECTION OFFSET offset OF json MATCH OFFSET offset.
309 | IF sy-subrc IS NOT INITIAL.
310 | throw_error.
311 | ENDIF.
312 | pos = offset - 1.
313 | IF json+pos(1) EQ '\'. " escaped quotes inside -> fallback on advanced processing
314 | lcl_util=>read_string( EXPORTING json = json mark = mark CHANGING text = &1 offset = offset ).
315 | ELSE.
316 | match = offset - mark.
317 | &1 = json+mark(match).
318 | ENDIF.
319 | ENDIF.
320 | offset = offset + 1.
321 |
322 | END-OF-DEFINITION.
323 |
324 | DEFINE eat_number.
325 | mark = offset.
326 | while_offset_cs '0123456789+-eE.' offset.
327 | match = offset - mark.
328 | &1 = json+mark(match).
329 | END-OF-DEFINITION.
330 |
331 | DEFINE eat_bool.
332 | mark = offset.
333 | while_offset_cs 'aeflnrstu' offset.
334 | match = offset - mark.
335 | IF json+mark(match) EQ 'true' ##NO_TEXT.
336 | &1 = c_bool-true.
337 | ELSEIF json+mark(match) EQ 'false' ##NO_TEXT.
338 | IF type_descr IS BOUND AND mv_bool_3state CS type_descr->absolute_name.
339 | &1 = c_tribool-false.
340 | ELSE.
341 | &1 = c_bool-false.
342 | ENDIF.
343 | ELSEIF json+mark(match) EQ 'null' ##NO_TEXT.
344 | CLEAR &1.
345 | ENDIF.
346 | END-OF-DEFINITION.
347 |
348 | DEFINE eat_char.
349 | IF offset < length AND json+offset(1) EQ &1.
350 | offset = offset + 1.
351 | ELSE.
352 | throw_error.
353 | ENDIF.
354 | END-OF-DEFINITION.
355 |
356 | DEFINE create_regexp.
357 | " first try PCRE
358 | TRY.
359 | CALL METHOD cl_abap_regex=>('CREATE_PCRE')
360 | EXPORTING
361 | pattern = &2
362 | RECEIVING
363 | regex = &1. "#EC NOTEXT
364 | CATCH cx_sy_dyn_call_error.
365 | CREATE OBJECT &1
366 | EXPORTING
367 | pattern = &2 ##REGEX_POSIX ##NO_TEXT. "#EC NOTEXT
368 | ENDTRY.
369 | END-OF-DEFINITION.
370 |
--------------------------------------------------------------------------------