Expand my Community achievements bar.

Learn about Edge Delivery Services in upcoming GEM session
SOLVED

Flex Remoting - Deeply Nested ArrayCollections not Stongly Typed

Avatar

Level 2

I have a Flex 3 app which uses remoting. All works relatively well except for the following...

I have a deeply nested object graph as follows:

( btw: each VO uses [RemoteClass(alias="com.mypackage.xxx")] meta tags )

The main class is called "BrandVO" which contains an ArrayCollection on ProductLineVOs.

each "ProductLineVO" has an ArrayCollection of "ProductVO"s

each "ProductVO" has an ArrayCollection of "CaseVO"s, "UseVO"s, and  "IngredientVO"s.

Pseudo Graphically:

+-BrandVO

+----ProductLineVO

+-------ProductVO

--------------CaseVO

--------------UseVO

--------------IngredientVO

+-------ProductVO

--------------CaseVO

--------------UseVO

--------------IngredientVO

+----ProductLineVO

+-------ProductVO

--------------CaseVO

--------------UseVO

--------------IngredientVO

+-------ProductVO

--------------CaseVO

--------------UseVO

--------------IngredientVO

...etc...

When I make the remoting call (AMF3) , I properly recieve back an ArrayCollection of BrandVOs...

(Debugging shows each item in the ArrayCollection [0]...[n] has a Value com.mypackage.BrandVO(@1234etc)

Expanding each BrandVO shows properly typed nested ProductLineVO elements...

(Debugging shows each item in the ArrayCollection [0]...[n] has a Value com.mypackage.ProductLineVO(@1234etc)

Expanding each ProductLineVO shows properly typed nested ProductVO elements...

(Debugging shows each item in the ArrayCollection [0]...[n] has a Value com.mypackage.ProductVO (@1234etc)

The problem then is when expanding each ProductVO, the nested CaseVOs, UseVOs, and IngredientVOs ( although they contain the proper data) are typed generically.

(Debugging CaseVOs shows each item in each ArrayCollection [0]...[n] has a Value Object (@1234etc )

(Debugging UseVOsshows each item in each ArrayCollection [0]...[n] has a Value Object (@1234etc )

(Debugging IngredientVOs shows each item in each ArrayCollection [0]...[n] has a Value Object (@1234etc )

I don't know why all the other VO's get typed correctly but CaseVOs, UseVOs, and IngredientVOs fail to be stongly typed and default to generic Objects.

I have checked the returning remoting call data ( with ServiceCapture ) and the objects coming back do have the correct types. The problem seems to be at the Flex level.

Anyone know why this would happen?

Is there a limit to object grpah depth beyind which objects get typed generically?

I thought perhaps is was because the ProductVO has 3 ArrayCollections ( CaseVOs, UseVOs, and IngredientVOs ), so I tried removing all but CaseVO and had the same problem... CaseVO was typed as Object.

Any help/thoughts would be gratly appreciated.

~e

1 Accepted Solution

Avatar

Correct answer by
Level 3

I'm feeling guilty for not asking you if you did that. Yes you need to do that, because if you don't reference a class in your app, the Flex compiler doesn't include it into the  ABC - SWF.

View solution in original post

12 Replies

Avatar

Level 3

Probably the AS3 and the RO VO don't match, the remote alias maybe, a var type isn't properly set, could be several reasons. I'll make a test to receive only CaseVO and see it's properly sent.

Avatar

Level 2

Hey Michael,

Thank you for the quick response and helpful suggestion.

Sure enough, if I just call for a CaseVO, it still is generically typed as an Object; so your theory that Flex cannot match up the RO and VO seems to be the correct. I have reviewed the code however and can't see any discrepancies. If you have a moment, can you take a look at the following and see if any errors seem evident:

The Flex VO

==========

package com.mypackage.vo
{                                           
   [RemoteClass(alias="com.mypackage.admin.vo.CaseVO")]
   public class CaseVO
   { 
        public var caseId:int;
        public var orderNumber:String;
        public var productId:int;
        public var qty:int;
        public var caseWeight:Number;
        public var measurementUnitId:int;


        public function CaseVO()
        {
        }

   }
}


The Romote Java RO

Notes:

1. The RO package is different then the VO package ( there is an extra "admin" dir component )

    but that shouldn't matter should it?

    ( since the proper mapping is declared in the "alias=" of the VO )

2. I tried it w/o the serialVersionUID proerty and method

3. I tried it w/o the equals and hashCode methods

=================================================

package com.mypackage.admin.vo;


public class CaseVO implements Serializable
{
   public static final long serialVersionUID = 1L;


   private int caseId;
   private String orderNumber;
   private int productId;
   private int qty;
   private float caseWeight;
   private int measurementUnitId;

   public static long getSerialVersionUID() { return serialVersionUID;}

   public int getCaseId() { return caseId; }
   public void setCaseId(int caseId) {this.caseId = caseId;}
   public String getOrderNumber() {return orderNumber;}
   public void setOrderNumber(String orderNumber) {this.orderNumber = orderNumber; }
   public int getProductId() {return productId;}
   public void setProductId(int productId) {this.productId = productId;}
   public int getQty() { return qty;}
   public void setQty(int qty) {this.qty = qty;}
   public float getCaseWeight() {return caseWeight; }
   public void setCaseWeight(float caseWeight) {this.caseWeight = caseWeight; }
   public int getMeasurementUnitId() {return measurementUnitId;}
   public void setMeasurementUnitId(int measurementUnitId) { this.measurementUnitId = measurementUnitId; }

   public boolean equals(Object other)
   {
        if (this == other) return true; 
        if ( !(other instanceof CaseVO) ) return false;
 
        final CaseVO case = (CaseVO) other;
        if ( case.getCaseId() != getCaseId() ) return false;
        if ( !case.getOrderNumber().equals(getOrderNumber()) ) return false;

        return true;
   }


   public int hashCode()
   {
        int result;
        result = getOrderNumber().hashCode();
        result = 29 * result + getCaseId();
        return result;
   }


}

Thanks,

~e

Avatar

Level 3

After a very quick look I noticed that the package name

package com.mypackage.vo

and the RO alias don't match in structure

[RemoteClass(alias="com.mypackage.admin.vo.CaseVO")]

Try moving the file to com.mypackage.admin.vo

Avatar

Level 2

Thanks again for the feedback.

I tried what you suggested and still have the same problem.

Plus the other VO's follow the same convention and they seem to corrolate to the ROs w/o a prob.

Very strange.

~e

Avatar

Level 3

Could you post another Java VO and AS VO that actually works. A suggestion if you're using the Adobe Forums interface there's this icon >>  if you click it, and select Syntax Highlighting - Java your code will look better and it'll be easier to read.

package com.mypackage.admin.vo;


public class CaseVO implements Serializable
{
   public static final long serialVersionUID = 1L;




   private int caseId;
   private String orderNumber;
   private int productId;
   private int qty;
   private float caseWeight;
   private int measurementUnitId;



   public static long getSerialVersionUID() { return serialVersionUID;}



   public int getCaseId() { return caseId; }
   public void setCaseId(int caseId) {this.caseId = caseId;}
   public String getOrderNumber() {return orderNumber;}
   public void setOrderNumber(String orderNumber) {this.orderNumber = orderNumber; }
   public int getProductId() {return productId;}
   public void setProductId(int productId) {this.productId = productId;}
   public int getQty() { return qty;}
   public void setQty(int qty) {this.qty = qty;}
   public float getCaseWeight() {return caseWeight; }
   public void setCaseWeight(float caseWeight) {this.caseWeight = caseWeight; }
   public int getMeasurementUnitId() {return measurementUnitId;}
   public void setMeasurementUnitId(int measurementUnitId) { this.measurementUnitId = measurementUnitId; }



   public boolean equals(Object other)
   {
        if (this == other) return true;
        if ( !(other instanceof CaseVO) ) return false;

        final CaseVO case = (CaseVO) other;
        if ( case.getCaseId() != getCaseId() ) return false;
        if ( !case.getOrderNumber().equals(getOrderNumber()) ) return false;

        return true;
   }




   public int hashCode()
   {
        int result;
        result = getOrderNumber().hashCode();
        result = 29 * result + getCaseId();
        return result;
   }




}

Avatar

Level 2

Hey Michael, this code works:

BTW - I changed Flex package path to match that on server.

Flex VO

======

package com.mypackage.admin.vo
{
    import mx.collections.ArrayCollection;
   
    [RemoteClass(alias="com.mypackage.admin.vo.BrandVO")]
    public class BrandVO
    {
        public var brandId:int;
        public var name:String;
        public var displayName:String;
       
        [Bindable]
        public var productLine:ArrayCollection = new ArrayCollection();
       
        public function BrandVO(){}

    }
}

Java RO

======

package com.mypackage.admin.vo;

import java.util.List;
import java.util.ArrayList;
import java.io.Serializable;

public class BrandVO implements Serializable
{

    private int brandId;
    private String name;
    private String displayName;
    private List<ProductLineVO> productLine = new ArrayList<ProductLineVO>();
   
    public static final long serialVersionUID = 1L;

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }
   
    public BrandVO(){}
   
   
    public int getBrandId() {
        return brandId;
    }
    public void setBrandId(int brandId) {
        this.brandId = brandId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
       
    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }
   
    public List<ProductLineVO> getProductLine() {
        return productLine;
    }

    public void setProductLine(List<ProductLineVO> productLine) {
        this.productLine = productLine;
    }

    public void addProductLine( ProductLineVO productLine){
        this.productLine.add(productLine);
    }

    public boolean equals(Object other)
    {
        if (this == other) return true;
       
        if ( !(other instanceof BrandVO) ) return false;
       
        final BrandVO brand = (BrandVO) other;
        if ( brand.getBrandId() != getBrandId() ) return false;
        if ( !brand.getName().equals(getName()) ) return false;
       
        return true;
    }
   
    public int hashCode()
    {
        int result;
        result = getName().hashCode();
        result = 29 * result + getBrandId();
        return result;
    }
   
}

Avatar

Level 3

They look kinda the same, Java isn't my forte , the only thing that I see different is that in your working java VO you have an import and in the one that doesn't you don't have that import. I don't know if that's important at all.

Avatar

Level 2

Thanks for looking at the code.

The import(s) weren't required for the other example ( since the first example didn't need to use a List or ArrayList ), so that's ok.

According to the Adobe docs, all you need to ensure the RO and VO match up is the same # of properties, and the same property names ( case sensitive ). AFAIK, all looks ok.

Too bad you cannot use Meta tags like you can with Flex Arrays (  [ArrayElementType("...")] ) with ArrayCollections to aid in the matching... or use generics ArrayCollection<T>.

May have to convert it all to SOAP then if I don't stumble on a solution.

Thanks again for the all the help. Just wanted to ensure I wasn't missing something obvious.

~e

Avatar

Level 2

I think I stumbled on a soltuotion:

from: http://flexdiary.blogspot.com/2008/11/thoughts-on-remoting.html

"I think the real reason the serialization wasn't working was the last thing I discovered, which is that you have to have a dummy variable typed as your custom class in the same class as the RemoteObject that you're using to retrieve the values. This makes sure that the class definition gets compiled into the class where it can be used."

If I add a Dummy Variable in my Flex ProductVO:

public var dummy:CaseVO;

 

[Bindable]
public var cases:ArrayCollection = new ArrayCollection();

then the returned RO gets properly deserialized.

Avatar

Correct answer by
Level 3

I'm feeling guilty for not asking you if you did that. Yes you need to do that, because if you don't reference a class in your app, the Flex compiler doesn't include it into the  ABC - SWF.

Avatar

Level 2

No worries, all help appreciated. The world makes sense again and I can enjoy my weekend .

Thanks again for the help!

~e