2
votes

Our Flex web application uses RemoteObject connections to send a custom class object to PHP. This object has multiple depth levels, with nested objects inside the main one. We found a limit on the number of nested levels allowed, no matter how complex is the object. When reached, the serialization made by the RemoteObject before sending the data crashes with this error:

TypeError: Error #1034: Type Coercion failed: cannot convert Object@7e30f89 en mx.messaging.messages.IMessage.

If we send a lighter object (removing all of its properties), the same occurs. In example:

var params:Object = {};
params['test'] = {0:{1:{2:{3:{4:{5:{6:{7:{8:{9:{10:{11:{12:{13:{14:{15:{16:{17:{18:{19:{20:{21:{22:{}}}}}}}}}}}}}}}}}}}}}}}};
remoteObject.runService(params);

If the object has less levels, it works:

params['test'] = {0:{1:{2:{3:{4:{5:{6:{7:{8:{9:{10:{11:{12:{13:{14:{15:{16:{17:{18:{19:{20:{21:{}}}}}}}}}}}}}}}}}}}}}}};

What it seems is that the RemoteObject allows a maximum nested levels depth of 24, maybe 23. After that, it crashes. Sending in JSON is not an option, as I lose all the typed classes and objects inside the main object.

Does anyone have any idea on how to face this?

Thanks in advance!

1
24 levels of object graph nesting? Good lord. Generally I try to flatten my objects before sending them to a webapp. It makes the data structure more accessible and less fragile when considering backend changes.Black Dynamite
Just to clarify: the FLEX application allows the user to design a network, adding multiple nodes and properties. Each node has also multiple children nodes and so on. The depth is unknown until the user finishes the design. The catched error said: "couldn't recurse deeper on object. Probably a looped reference" I managed to make this work by upgrading the AMFPHP version from 2.1 to 2.2. It seems the old plugin "AmfphpCustomClassConverter" was causing this issue. It has been now renamed to "AmfphpVoConverter" and it works fine now, no matter how many levels I set on the sent object.spacorum
Ah, that makes sense. If anything is going to have a child/parent relationship its going to be a network diagram. I'm glad you found the solution and even posted it here, it seems like a rare and highly involved problem.Black Dynamite

1 Answers

0
votes

Persisting 24 levels in a tree structure without dividing the tree into more manageable chunks is not a very sustainable idea. To convert your tree to a flat structure you should give each node in your tree a UID, an array of its children and, if need be, a reference to its parent.

As you send your flattened tree to the server you then serialize the objects so that each node you send to the server is just an untyped object containing its UID and the UIDs of its children and parent. This is a completely flat structure and there is no need for nesting. When you read from the server you reconstruct the tree structure on the client.

public class Node {
    public var uid:String;
    public var children:Array = [];
    public var parent:String;

    public function addChild(node:Node):void
        children.push(node);
        node.parent = this;
    }
}           

// Elsewhere..

// Convert tree nodes into flat array of raw objects
public function serialize(rootNode:Node):Array {
    // Remove tree structure.
    var flattenedNodes:Array = flatten(rootNode);

    // Convert to untyped objects using only string UIDs to identify children and parents.
    var untypedNodes:Array = [];
    for each (var node:Node in flattenedNodes) {
        var childIds:Array = [];
        for each (var child:Node in node.children) {
            childIds.push(child.uid);
        }

        var untypedNode:Object = {
            uid: node.uid,
            parent: node.parent ? node.parent.uid : null,
            children: childIds
        };
        untypedNodes.push(untypedNode);
    }

    return untypedNodes;
}

private function flatten(node:Node):Array {
    var result:Array = [node];
    for each (var child:Node in node.children) {
        result = result.concat(flatten(child));
    }
    return result;
}

// Convert a flat array of raw objects back into tree nodes
public function deserialize(items:Array):Node {
    var root:Node;
    var current:Node;
    var item:Object;
    var uidToNodeMap:Dictionary = new Dictionary();
    // Add all nodes to a map by uid
    for each (item in items) {
        current = new Node();
        current.uid = item.uid;
        uidToNodeMap[item.uid] = current;
        if (!item.parent) {
            root = current;
        }
    }
    // Build tree by replacing ids with actual nodes
    for each (item in items) {
        current = uidToNodeMap[item.uid];
        current.parent = uidToNodeMap[item.parent];
        for each (var childId:String in item.children) {
            current.children.push(uidToNodeMap[childId]);
        }
    }
}