2

I'm reasonably confident in my first generics container, but stuck on how to word the casting on the client side. This is what was working before I got involved in learning <T> stuff:

CommonNounContainer typeContainer = new Json().fromJson(CommonNounContainer.class, result);

I was looking at having to create a different container for each class, and that doesn't seem like good design. Below is my updated, non-working attempt to read in my new generics container:

JSONContainer<CommonNoun> typeContainer = new Json().fromJson(JSONContainer.class, result);

My IDE doesn't care for this phrasing, noting:

Type safety: The expression of type JSONContainer needs unchecked conversion to conform to JSONContainer

When executed, my err log reads:

result = {"myObject":{"cid":{"oid":129},"name":"technology","form":1},"children":[]}
com.badlogic.gdx.utils.SerializationException: Field not found: cid (java.lang.Object)
Serialization trace:
{}.myObject.cid
myObject (semanticWeb.rep.concept.JSONContainer)
    at com.badlogic.gdx.utils.Json.readFields(Json.java:854)
    at com.badlogic.gdx.utils.Json.readValue(Json.java:1011)
    at com.badlogic.gdx.utils.Json.readFields(Json.java:863)
    at com.badlogic.gdx.utils.Json.readValue(Json.java:1011)
    at com.badlogic.gdx.utils.Json.fromJson(Json.java:789)
    at com.b2tclient.net.Communicator$2.handleHttpResponse(Communicator.java:95)
    at com.badlogic.gdx.net.NetJavaImpl$2.run(NetJavaImpl.java:224)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:830)

I'm sure there's some way I'm supposed to include a reference to the CommonNoun type to the right of the equals sign, but I haven't been able to figure it out. How do I do it? There's lots of applicable posts concerning generics, casting, JSON, and stripping away of class information. One of them I tried to follow that wasn't about the casting above regarded adding the T class as a private variable within the container during construction:

How do I get a class instance of generic type T?

but I ran into similar syntax issues trying to refer to the class correctly, just in a different spot along the process. I have my doubts, too, that I can read this class variable from the JSON file before telling JSON how to classify the information in the file.

Javadoc for the fromJson(Class<T>, String) method:

Type Parameters:
   <T> 
Parameters:
   type May be null if the type is unknown.
   json 
Returns:
   May be null. 

I may already have a viable answer submitted by deduper, but, as requested, here are the CommonNounContainer and JSONContainer classes:

import java.util.ArrayList;

public class CommonNounContainer {

   private CommonNoun myCommonNoun;
    private ArrayList<CommonNounContainer> children;
    public CommonNounContainer(CommonNoun concept) {
        myCommonNoun = concept;
        children = new ArrayList<CommonNounContainer>();
    }

    //Creates an empty shell.  This would be for categories you want to group by, but not display/select in the select box.
    public CommonNounContainer() {
        children = new ArrayList<CommonNounContainer>();        
    }
    
    public void addChildren(ArrayList<CommonNounContainer> newChildren) {
        children.addAll(newChildren);
    }

    public void addChild(CommonNoun concept) {
        children.add(new CommonNounContainer(concept));
    }
        
    public ArrayList<CommonNounContainer> getChildren() {
        return children;
    }
    
    public CommonNoun getValue() {
        return myCommonNoun;
    }
    
    public boolean hasChildren() {
        if (children.size() > 0) return true;
        else return false;
    }
    
    public String toString() {
        return myCommonNoun.toString();
    }
} 


public class JSONContainer<T> {

    private T myObject;
    private ArrayList<JSONContainer<T>> children;
//    public Class<T> typeParameterClass;
    
    public JSONContainer() {

    }
    
    public JSONContainer(T anObject) {
        myObject = anObject;
        children = new ArrayList<JSONContainer<T>>();
    }

/*  public JSONContainer(T anObject, Class<T> typeParameterClass) {
        myObject = anObject;
        children = new ArrayList<JSONContainer<T>>();
        this.typeParameterClass = typeParameterClass;
    }
*/
    
    public void addChildren(ArrayList<JSONContainer<T>> newChildren) {
        children.addAll(newChildren);
    }

    public void addChild(T concept) {
        children.add(new JSONContainer<T>(concept));
    }
        
    public ArrayList<JSONContainer<T>> getChildren() {
        return children;
    }
    
    public T getValue() {
        return myObject;
    }
    
    public boolean hasChildren() {
        if (children.size() > 0) return true;
        else return false;
    }
    
    public String toString() {
        return myObject.toString();
    }
}

Additional classes requested:

public class CommonNoun extends Concept {
        
    /**
     * 
     */
    private static final long serialVersionUID = 6444629581712454049L;

    public CommonNoun() {
        super();
    }
    
    public CommonNoun(String name, ConceptID cidIn) {
        super(name, cidIn);
        this.form = ConceptDefs.COMMON_NOUN;
    }
    
}

public class Concept implements Serializable {
    
    /**
     * 
     */
    private static final long serialVersionUID = 2561549161503772431L;
    private ConceptID cid = null;
    private final String name;
    Integer form = 0;
    
//    ArrayList<ProperRelationship> myRelationships = null;

    
/*  @Deprecated
    public Concept(String name) {
        this.name = name;
    }*/
    
    public Concept() {
        name = "";
    }

    public Concept(String name, ConceptID cidIn) {
       // this(name);
        this.name = name;
        cid = cidIn;
    }

    /*
     * This should be over-ridden by any subclasses
     */
    public Integer getForm() {
        return form;
    } 
            
    public ConceptID getID() {
        return cid;
    }
    
    public void setID(ConceptID cidIn) {
        cid = cidIn;
    }
    
    //this doesn't make any sense.  Throw exception?
    public String getName() {
        return name;
    }

    public boolean isCommon() {
        return true;
    }
    
    /**
     *
     * @return
     */
    @Override
    public String toString() {
        return getName() + "(" + cid.toString() + ")";
    }
    
    public boolean equals(Concept other) {
        return ((getID().equals(other.getID())));
    }
    
}

public class ConceptID implements Serializable {

    long oid;
    
    public ConceptID() {
        oid = -1;
    }
    public ConceptID(long oid) {
        this.oid = oid;
    }
    
    public long getValue() {
        return oid;
    }
    
    /**
     *
     * @return
     */
    @Override
    public String toString() {
        return Long.toString(oid);
    }
    
    public Long toLong() {
        return Long.valueOf(oid);
    }

    
    public boolean equals(ConceptID other) {
        return (oid == other.getValue());
    }
    
    /**
     * Factory model for generating ConceptIDs
     * 
     * This one is here as a convenience as many IDs come in as a String from web POSTs
     * @param idAsString
     * @return
     */
    static public ConceptID parseIntoID(String idAsString) {
        ConceptID returnID = null;
        try {
            returnID = new ConceptID( Long.parseLong(idAsString) );
        } catch (Exception e) {
            System.err.println("Expected the string, " + idAsString + ", to be Long parsable.");
            e.printStackTrace();
        }
        return returnID;

    }
  • Since there are several JSON libraries, it's better if you specify the one you are using. From the logs it seems `com.badlogic.gdx`. – Marc Aug 18 '20 at 05:05
  • Yes, that's it. import com.badlogic.gdx.utils.Json; – RocketScienceGuy Aug 18 '20 at 05:07
  • I added the applicable javadoc to the bottom of the question in case that saves anyone a step. – RocketScienceGuy Aug 18 '20 at 05:16
  • 1
    Can you also share the classes `CommonNounContainer` and `JSONContainer`? – Marc Aug 18 '20 at 06:09
  • Thanks for sharing _`CommonNounContainer`_ & _`JSONContainer`_. I was able to reproduce your original error verbatim. The root cause is: *The format of your original `result` string isn't acceptable to that JSON library's reader*. I've established that it'll only succeed at parsing input strings if they're in that library's very specific (*non-standard*) format. P.S. It would be useful to also see the details of how your _`CommonNoun`_ & _`Cid`_ classes are implemented. I took a wild guess at them with the *stand-ins* in [*my answer*](https://stackoverflow.com/a/63467119/4465539). TIA. – deduper Aug 18 '20 at 17:24

1 Answers1

1

TL;DR:

Proposed Fix…

  1. System.out.println( new Json( ).toJson( new JSONContainer<>( ... ) ) to see the correct string format of a JSONContainer's JSON.
  2. Make sure your result input argument to Json.fromJson(Class<T>, String) is in the same format printed out in 1.
    • e.g. {myObject:{class:CommonNoun,cid:{oid:139},name:Jada Pinkett Smith,form:69},children:[{myObject:{class:CommonNoun,cid:{oid:666},name:Jaden Pinkett Smith,form:-666},children:[]},{myObject:{class:CommonNoun,cid:{oid:69},name:Willow Pinkett Smith,form:69},children:[]}]}


The long answer

My IDE doesn't care for this phrasing, noting:

Type safety: The expression of type JSONContainer needs unchecked conversion to conform to JSONContainer

It's the compiler warning you about heap pollution. The IDE merely translated this compiler warning (which is what you'd see on the command line)…

...Communicator.java uses unchecked or unsafe operations.
...Recompile with -Xlint:unchecked for details.

…into the more user-friendly message the IDE showed you.

It is only a warning; not an error. To make that warning go away, change this: JSONContainer<CommonNoun> typeContainer = ... to this: JSONContainer typeContainer = ...

When executed, my err log reads:

result = {"myObject":{"cid":{"oid":129},"name":"technology","form":1},"children":[]}
com.badlogic.gdx.utils.SerializationException: Field not found: cid (java.lang.Object)...

The most likely cause of that error is — like the error message says — either your JSONContainer class or your CommonNoun class does not have the cid field that is present in the JSON string you're trying to deserialize.

I was able to reproduce that error with this…

...
private static final String JADEN_AS_JSON = "{jden:{class:CommonNoun,person:Jaden,place:Hollywood,thing:HashBeen}}";

private static final String JADEN_FAILS_AS_ACTOR = "{jden:{class:CommonNoun,person:Jaden,place:Hollywood,thing:HasBeen, cid:{oid:129} }}";

static public void main( String ... args ){
    
    out.printf( "%1$22s%n", "foo");
    
    JSONContainer< CommonNoun > wtf = new JSONContainer< > ( );
    
    CommonNoun wtBrattyF = new CommonNoun( "Jaden Pinkett Smith", "Hollywood", "HasBeen" );
    
    wtf.setJden( wtBrattyF );
    
    out.printf( "%1$42s%n", wtf );
    
    Json jden = new Json();
    
    out.printf("%1$59s%n", jden.toJson( wtf ) );
            
    JSONContainer wtReifiableF = jden.fromJson(JSONContainer.class, JADEN_AS_JSON); /* This is fine */        
    
    out.printf("%1$59s%n", jden.toJson( wtReifiableF ) );
            
    JSONContainer/*< CommonNoun >*/ wtUnReifiableF = jden.fromJson( JSONContainer.class, JADEN_AS_JSON );
    
    wtUnReifiableF = jden.fromJson( JSONContainer.class, JADEN_FAILS_AS_ACTOR ); /* This causes the error you reported */
}
...

Early on it succeeds; but later on it fails…

JSONContainer [ jden: CommonNoun [ person: Jaden Pinkett Smith, place: Hollywood, thing: HasBeen ] ]
{jden:{class:CommonNoun,person:Jaden Pinkett Smith,place:Hollywood,thing:HasBeen}}
{jden:{class:CommonNoun,person:Jaden,place:Hollywood,thing:HashBeen}}

Exception in thread "main" com.badlogic.gdx.utils.SerializationException: Field not found: cid (CommonNoun)
Serialization trace:
{}.jden.cid
jden (JSONContainer)
    at com.badlogic.gdx.utils.Json.readFields(Json.java:893)
    at com.badlogic.gdx.utils.Json.readValue(Json.java:1074)
    at com.badlogic.gdx.utils.Json.readFields(Json.java:902)
    at com.badlogic.gdx.utils.Json.readValue(Json.java:1074)
    at com.badlogic.gdx.utils.Json.fromJson(Json.java:829)
    at DeduperAnswer.main(DeduperAnswer.java:33)


I have now confirmed by experimentation that given the existence of a Cid class…

public class Cid { 
    
    int oid;        

    /* ... getter and setter elided ... */
}

… And given the existence of a CommonNoun class that HAS A Cid

public class CommonNoun { 
    
    Cid cid;
    
    String name;
    
    int form;

    /* ... getters and setters elided ... */
}

…Then trying to deserialize a JSONContainer from a result that has the following value, will produce the exact same error you originally reported…

result = {"myObject":{"cid":{"oid":129},"name":"technology","form":1},"children":[]}

If your actual CommonNoun class is implemented like my stand-in above (with a Cid field), then you need to retry your json.fromJson(Class<?>, String) call with your result string formatted like…

{myObject:{class:CommonNoun,cid:{oid:139},name:Jada Pinkett Smith,form:69},children:[{myObject:{class:CommonNoun,cid:{oid:666},name:Jaden Pinkett Smith,form:-666},children:[]},{myObject:{class:CommonNoun,cid:{oid:69},name:Willow Pinkett Smith,form:69},children:[]}]}
deduper
  • 1,944
  • 9
  • 22
  • I've added the requested classes, though I think you figured out the internal variables already. I also think you've shown me my error: I'd mistakenly assumed all Json tools would be governed by the same standard when it came to output formatting. I built my semantic web on the server first, and later learned how to use libGDX to build my client. There's a lot of re-implemented classes in there, presumably to help with garbage collection or efficiency in libGDX's execution model. Right now I'm trying to bring one or the other Json class over to the other side. – RocketScienceGuy Aug 19 '20 at 01:10