FoxPro Programming
aMembers() oddity in wwJsonSerializer.WriteObject()
Gravatar is a globally recognized avatar based on your email address. aMembers() oddity in wwJsonSerializer.WriteObject()
  Carl Chambers
  All
  Apr 11, 2024 @ 11:20am

For years, I have had no problems serializing (with wwJsonSerializer) an object with the following structure and it's still working just fine.

Parts Breakdown (object)
  Drawings (collection)
    Drawing (object)
      Callouts (collection)
        Callout (object)
          Parts (collection)
            Part (object)

Now I'm trying to serialize each Parts collection individually by passing just the Parts collection object.

lcJSON = loJSONSerializer.Serialize(loPartsCollection)

When I do this with a collection that has 3 parts, lcJSON looks like this...

[ {}, {}, {} ]

In this screenshot, loObject is the first Part object in the Parts collection passed. Although it clearly has several properties, AMEMBERS() in wwJSONSerializer.WriteObject() returns 0 to lnProperties.

When serializing the entire Parts Breakdown object, everything works just fine.
But, when passing just a Parts collection (from the exact same Parts Breakdown object), this happens.

Any idea what's causing this?
Thanks, Carl

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Rick Strahl
  Carl Chambers
  Apr 11, 2024 @ 12:30pm

If you're referencing the screenshot laMembers looks potentially like an uninitialized array. That might be an array that had values in it at some point but they got removed via ADEL() or something else, but the debugger still sees it as an array.

When you hit this code what does laMembers[1] return when you print it to the console? I suspect it will error.

The debugger is not always accurate when it comes to arrays.

That's why I avoid arrays like the plague and use collections instead, even though they can be less efficient.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Carl Chambers
  Rick Strahl
  Apr 11, 2024 @ 12:53pm

Hi Rick,

In the Trace window, you'll notice that the pointer is on the line immediately after...

lnProperties = AMEMBERS(laMembers,loObject)

... in wwJSONSerializer.WriteObject().

In the Locals window, loObject is, in fact, an object with 10 properties.
However, AMEMBERS(), which just executed, returns 0 instead of 10 and laMembers (created by AMEMBERS()) has 1 element and it is .F. (at the bottom of the Locals window).

I cannot figure out how AMEMBERS() does not recognize the 10 properties that are clearly shown in the Locals window. I can't see anything wrong with loObject that should cause AMEMBERS() to fail.

Carl

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Rick Strahl
  Carl Chambers
  Apr 11, 2024 @ 02:23pm

I don't either. You think you can set up a repro case that fails and that I can run?

The only thing I can think of is that there's perhaps an error that occurs and that's eaten? Do you have an OnError() handler somewhere that might be bypassing a failure?

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Carl Chambers
  Rick Strahl
  Apr 13, 2024 @ 10:26am

I was not able to reproduce this in a test program.
I did find something interesting though as commented in this snippet.

FOR EACH loPart IN loPartsCollection
  *
  *  Check AMEMBERS() count
  *
  lnProperties = AMEMBERS(laProps,loPart)	&& Returns 0
  
  *
  *  Tweek a couple properties
  *
  loPart.part_number = TRIM(loPart.part_number)
  loPart.description = TRIM(loPart.description)
  
  *
  *  Check AMEMBERS() count again
  *
  lnProperties = AMEMBERS(laProps,loPart)	&& Returns 2
NEXT loPart

Still have not come up with a test program to reproduce this.

The strange part is that loPartsCollection is a property along the chain in a much larger object as I illustrated in my OP.
All of the loPart objects encountered when serializing the entire PartsBreakdown object are processed with no problem.
Only when plucking each PartsCollection out of the entire PartsBreakdown object and serializing them individually does the failure occur.

I ended up handcrafting the JSON to meet my needs.

Thanks for your response,
Carl

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Rick Strahl
  Carl Chambers
  Apr 13, 2024 @ 03:04pm

Is that object a COM object perhaps? That's the behavior you would see for a IDispatch object. But in that case it also wouldn't show in the Debugger.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Carl Chambers
  Rick Strahl
  Apr 15, 2024 @ 05:59pm

Nope - all Fox objects.
This...

lcJSON = wwJsonSerializer.Serialize(loPartsBreakdown)

...works fine (has for years).

This on the exact same loPartsBreakdown fails...

FOR EACH loDrawing IN loPartsBreakdown.oDrawings
  FOR EACH loCallout IN loDrawing.oCallouts
    *
    *  Serialize the parts collection for this callout.
    *
    lcJSON = wwJsonSerializer.Serialize(loCallout.oParts)
      *
      *  This fails due to AMEMBERS() returning 0
      *  in wwJSONSerializer.WriteObject() for each
      *  part in the collection.
      *
  NEXT loCallout
NEXT loDrawing

I'm stumped.
But handcrafting the JSON for the Parts collection probably took less time than it would take to figure this out.
I was just curious if anyone had encountered this before and had found the cause.
Thanks again.

Carl

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Rick Strahl
  Carl Chambers
  Apr 15, 2024 @ 06:28pm

Can you use FOXOBJECT on your FOREACH loops? It sure acts like a COM object and FOXOBJECT forces the loop to treat objects like a FoxPro object.

FOR EACH loCallout IN loDrawing.oCallouts FOXOBJECT

Do it on both loops! I suspect retrieving the second collection from the first is where the problem starts with the mis-typing.

There's some tricky behavior in FOREACH that can cause objects to be turned into pseudo COM objects because FOREACH has to work with COMARRAY style objects. I bet that's what's happening.

That would also explain why it works if you serialize the entire object since it internally uses a FOR loop and pulls the instance.

I suspect if you use a FOR loop and retrieve by index it also would work.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Carl Chambers
  Rick Strahl
  Apr 17, 2024 @ 02:54pm

You nailed it, Rick - using FOXOBJECT solved it.
It's been years since I last looked at the FOR EACH help topic and I either forgot about the FOXOBJECT argument or I never really made note of it in the first place.

Thanks,
Carl

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Rick Strahl
  Carl Chambers
  Apr 18, 2024 @ 10:23am

Whenever you use FOREACH you should always use it with FOXOBJECT unless you know there may be a COM object in the mix. There's lots of weird stuff that happens if you don't , plus it's much faster as it internally omits a number of type checks.

I should take another look at my docs to make sure that I add it everywhere especially as it relates to wwJsonSerializer.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: aMembers() oddity in wwJsonSerializer.WriteObject()
  Carl Chambers
  Rick Strahl
  Apr 18, 2024 @ 10:56am

That's good to know. Thanks, Rick.

Carl

© 1996-2024