generate aa-xml-output from document
count in aa-xml-char-counter
on exception
display 'XML generate on exception 'xml-code
not on exception
display 'No exception'
*
end-xml
*
exit.
******************************************************************
*
* Z AVSLUTNING
*
******************************************************************
Z-AVSLUT SECTION.
MOVE ZERO TO RETURN-CODE
CONTINUE.
Note the fact that I init NOTERINGAR_RUBRIK1 to blanks in the b-init section. If you debug this program (we use Xpediter) and keep the NOTERINGAR_RUBRIK1 variable, you'll see that it gets set to blank (no surprise).
However as soon as you run the line "add 1 to ix-placeringsprofil"
it gets set to low values.
For this reason I have ANOTHER move ' ' to the variable AFTER the perform loop.
Here comes the rub. If I comment out the move ' ' after the perform, I get an exception on the XML generate (rc 417) since NOTERINGAR_RUBRIK1 now contains low values.
If I leave the move ' ' in, no exception.
My real problem is the following. As far as I can see it, I need to do one of the following options:-
1 Position my sub-variables in DOCUMENT such that they are placed "in the right order" (ie, place NOTERINGAR_RUBRIK1 above PLACERINGSPROFIL and then create the various values in the same order (ie, NOTERINGAR_RUBRIK1 first, then PLACERINGSPROFIL etc etc). Trouble is, I have quite a few of these groups under DOCUMENT and I can just see how the order would get out of wack somewhere down the line.
2 Define each group under DOCUMENT as its own 01 level variable and then generate each one of them and catenate them together
Here at the bank, we develop/program test using Micro Focus's Mainframe Express. They have an option called NOODOSLIDE which, basically, ensures that all tables "appear" to be defined in advance with the maximum number of entries possible. This results in the problem above being a non-starter. Trouble is, you then lift your programs to the "real" environment and your problems start.
Maybe there's simply a compiler option I could use that I'm not aware of, or maybe there's a PTF that hasn't been applied.
Any suggestions/comments greatly appreciated. _________________ Michael
You have what is called a Complex ODO (you have data-definitions following an ODO under the same 01-level, meaning those data-items are variably-located).
NOTERINGAR_RUBRIK1 exists only once, it has a fixed length, but it has 51 potential locations depending on ix-placeringsprofil. When you did you MOVE, you only set the first of those (or whatever ix-placeringsprofil happened to contain - actually, looking at ix-placeringsprofil, it has no defined value by the time you do the MOVE of space, it is packed-decimal, so you are either using NUMPROC(NOPFD) as a compile option, or you got (and continue to get) extremely lucky)).
The fix I suggest is to use fixed-length OCCURS for all your preparation of the data. Once that is complete, immediately before the generation of the XML (I'm guessing that is why you want the ODO, to not have "empty" entries) then, and only then, set up the data in the variable-length occurs.
It will greatly simplify things, take a lot of technical clutter away, leaving just the business processing and your program easier to understand.
The alternative, which is absolute standard, is to MOVE all (as in all) the items which follow an ODO-group to a location to save them. Change the ODO value. Then MOVE them back to their original names. The position of the data will have changed. To preserve the content, you have to go through that.
Micro Focus's ODOSLIDE does that processing for you (as I understand it). There is no IBM COBOL that has an equivalent option or functioning.
I'm curious as to why you have a mixture of usages for you loop-control/subscripting, but that's something else.
Joined: 02 Dec 2002 Posts: 629 Topics: 176 Location: Stockholm, Sweden
Posted: Wed Aug 12, 2015 2:26 am Post subject: Thanks William
In answer to your questions/comments.
NUMPROC(NOPFD) - correct, this is the default compiler option here
Quote:
The fix I suggest is to use fixed-length OCCURS for all your preparation of the data
Not sure if I get you here. Do you mean that I should have occurs 50 rather than occurs 0 to 50 depending on ? If so, I'll end up with a load of empty entries won't I (I found a code example that was neat and that did an XML PARSE after the GENERATE and removed all the empty entries, so that might be an option).
Again, if I understand you correctly, the idea of defining the NON-occcurring fields first and then the occurring ones would be a non-starter. The program is complicated enough (and changes a lot) so that you could never be SURE that some restructuring of it wouldn't ruin things.
Quote:
usages for you loop-control/subscripting
Basically, to my (naive ?) way of thinking, the code should be working. I get that nested occurs depending on could cause all sorts of problems, but not of the entries I have in the actual structure are what I would call nested.
Based on what you say, I see 2 options:
1 Define each occurring group as a 01-level group and generate each one and catenate them
2 Define them as occurs x (rather than occurs 0 to x) and then removing the empty entries.
Any other options you can think of? _________________ Michael
What I'm suggesting is that you use fixed-size OCCURS while you are "loading" and doing whatever else you need with the data.
Then. After all that is complete. Set up the table lengths and copy the data from the fixed-length table to the variable-length table, so you can generate the XML without having to remove unused items.
That way you have the data-manipulation made without the additional complexity of the ODO, and the XML generated without the need to remove "empty" items.
Your program will be smaller, simpler, and easier to understand (the ODOs only existing to ease the production of the XML).
Joined: 02 Dec 2002 Posts: 629 Topics: 176 Location: Stockholm, Sweden
Posted: Wed Aug 12, 2015 3:41 am Post subject:
Aha ..... (eureka moment). That would make it a LOT simpler I think. I'll certainly give it a go and get back to you. Thanks a lot _________________ Michael
Normally, our standard is NOT to use underscore characters in cobol names, but for some reason or other, the guys who develop the XML schemas use them for tag names. For that reason, I thought it was easier to use exactly the same names but simply preceded by a tb_
When filling the TB_ table elements, I use the/increase the same variable as defined for the XML document, ie. This means that if I add rows to tb_placeringsprofil, I increase the value in ix-placerinsgprofil for each new entry.
Now to the copying of the data from the fixed length table to the XML document. (note that I copy the items/tables in the same order they're defined in the actual XML document)
Code:
****
move tb_doc_datum to doc_datum
move tb_doc_blankett to doc_blankett
****
move tb_namn to namn
perform varying ix-1 from 1 by 1 until
ix-1 > ix-adress02
move tb_co_adress(ix-1) to co_adress(ix-1)
move tb_adress(ix-1) to adress(ix-1)
move tb_adressrad3(ix-1) to adressrad3(ix-1)
end-perform
****
perform varying ix-1 from 1 by 1 until
ix-1 > ix-kund-pnr
move tb_kund_persnr(ix-1) to kund_persnr(ix-1)
end-perform
****
perform varying ix-1 from 1 by 1 until
ix-1 > ix-kund-info
move tb_kund_col1(ix-1) to kund_col1(ix-1)
move tb_kund_col2(ix-1) to kund_col2(ix-1)
end-perform
****
perform varying ix-1 from 1 by 1 until
ix-1 > ix-placeringsprofil
move tb_placeringsprofil(ix-1)
to placeringsprofil(ix-1)
end-perform
et voilĂ . I can now generate the XML document without a load of problems.
Thanks again for the help and the (brilliant) suggestion.
(now to keep it to myself so as to be indispensable at work . Just kidding) _________________ Michael
Updating the ODO values as you go along will work, as will copying the final values once you know them. I prefer the latter, firstly because it used to be a more efficient way to do it (IBM compilers recalculated lengths with the ODO values changed, they now only do it when making a reference requires the current length of the table) and secondly because an unwary maintainer doesn't have to keep two things in step.
I'd also do the final MOVEs to the table in the order you chose, because it seems clearer. However, as long as all the ODO values are set before starting the MOVEs, the order does not matter in this type of case. As soon as you have code which changes a prior ODO value, whilst you already have data in the table, that is where you get the problem.
There is an Appendix in the Programming Guide which deals with Complex ODOs. I guess the use of ODO will be expanding, exactly as the use of XML generation expands. Many people trip up with ODOs, so good to have your example.
Note, the Complex ODO is an IBM Extension. from the Appendix on Extensions in the Language Reference:
Quote:
Complex OCCURS DEPENDING ON. [Standard COBOL 85 requires that an entry containing OCCURS DEPENDING ON be followed only by subordinate entries, and that no entry containing OCCURS DEPENDING ON be subordinate to an entry containing OCCURS DEPENDING ON.]
Just a follow-up on the uninitialised ix-placeringsprofil in your original example. I could tell you had NUMPROC(NOPFD) because with NUMPROG(PFD) (or the rarer NUMPROC(MIG)) you'd have got a S0C7 running that code.
I suspect the field not being initialised was down to you making the short program, rather than in the original, but with NUMPROC(NOPFD), the sign-fixing can make bad things (probable binary zeros in the field (likely), possibly whatever was lying around) good. Your field had it's sign (temporarily) "fixed", to be a proper packed-decimal zero, which was then used to calculate the length of the table (where affected) and the start-position of the items following it.
It's something to be aware of when using NUMPROC(NOPFD).
Noticed now, with COBOL V5, the Complex ODO is no longer in an Appendix, but in the body of the Programming Guide.
The problem in Kolusu's link, that of an index on a variable-length item, occurs only when an ODO is subordinate to another table (OCCURS or OCCURS DEPENDING ON). Yes, the length of the subordinate table changes, but it is the concomitant change in length of the item with the INDEXED BY which is the problem. True, you can't have one without the other, but the documentation is a little unclear.
The save, change ODO value, restore for the index is analagous to the save, change ODO value, restore to preserve data which follows an ODO within the same 01-level. However, that does not mean the index-preserving has to be done for all indexes on an ODO.
In Michael's final sample code, the index-preserving technique would not be required (and use would therefore confuse), as all the OCCURS items themselves are fixed-length.
Joined: 02 Dec 2002 Posts: 629 Topics: 176 Location: Stockholm, Sweden
Posted: Sun Aug 16, 2015 1:29 am Post subject:
William, just so there's no confusion, your last sentence "the index_preserving technique....." is only relevant with Cobol 5 ????? (Or am I missing something that would make code even easier in cobol 4) _________________ Michael
Sorry, no, the index-preserving technique is only relevant when a table is variably located (and only impacts when the start-position of the tale changes. This is the same in all versions of Enterprise COBOL. I'll try to clarify here, and look at editing the original.
The point about V5 was that the text had been "promoted" from an Appendix to the main body of the manual, perhaps supporting the idea of an expectation of Complex ODO usage becoming more common.
That is, if there is an ODO within a table (whether or not the table it is within is also an ODO).
Code:
01 some-numeric-item BINARY PIC 9(4) VALUE ZERO.
01 the-first-table.
05 table-entry occurs 10 times
indexed by entry-minus-1-X-length.
10 a-bit-of-stuff PIC X.
10 a-variable-length-table.
15 filler occurs 0 to 10 times
depending on some-numeric-item.
20 filler PIC X.
If entry-minus-1-X-length is SET to 1, the value contained in the index is zero. It has been calculated as entry (1) minus 1 (so zero) time length (1, the a-bit-of-stuff, a-variable-length-table has a length of zero, because some-numeric-item is zero).
If one is MOVEd to some-numeric-item, the length of table-entry becomes two.
That doesn't matter for the first entry in the table, because for the first entry, irrespective of the length of the item, an index always contains zero.
SET entry-minus-1-X-length to two. Two minus one is one, length of the table entry is two (the fixed part, of one, and the variable part of one (current value in some-numeric-item is one).
Now, if you want to add another entry to a-variable-length-table (add one to some-numeric-item), then the length of the table entry-name with the INDEXED BY is now three bytes, but the index is unchanged, so is not related to the correct data.
That is the problem. When the length of the subordinate table changes, the length of the entry-name with the INDEXED BY changes. But nothing automatically changes with the INDEXED BY item.
So you have not do it manually: save entry-number represented by the index-name (SET to aninteger); change the length of the subordinate table; SET the index-name to the the integer.
Don't be tempted to use USAGE INDEX, which would be normal when preserving an index value, because that holds the displacement (entry minus one multiplied by entry-length). The displacement is what has been invalidated by the change in length of the entry, due to the change in length of the subordinate table.
Thus, the issue affects any OCCURS which contains a subordinate (not a following) OCCURS DEPENDING ON. It is only in this subb-set of Complex ODO that the technique is required. In any other usage of ODO (Complex or not) it is not required, and will confuse (and consume CPU).
So, the issue did not affect your original coding (you had following ODOs, not subordinate).
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum