JSInterop and "JSON.stringify" method return "Converting circular structure to JSON"

66 views
Skip to first unread message

Marco Tenti (IoProgrammo88)

unread,
May 22, 2024, 4:32:49 AMMay 22
to GWT Users

I want to convert a complex java object to a json string on my GWT project

Main library used:

GXT: 4.1.0

GWT: 2.9.0

Domino Jackson: 1.0.4

I tried to to use the "JSInterop" togheter with the "JSON.stringify" 

Here the: "JSON" class (i use the one from the domino-jackson library version 1.0.4 , but it should be not important https://github.com/DominoKit/domino-jackson/blob/master/domino-jackson/src/main/java/org/dominokit/jackson/JSON.java):

 

/** JsInterop implementation to use the browser built in JSON object. */

@JsType(isNative = true, namespace = JsPackage.GLOBAL)

public class JSON {

  public static native String stringify(Object jsonObj);

}

Here all the objects structure i'm trying to convert (note i use name= "Object" for solve this strange issue https://groups.google.com/g/Google-Web-Toolkit/c/IpdzElRVkBc) :

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)

public class DocumentoPAC4DTOGWT {

   

@JsConstructor

public DocumentoPAC4DTOGWT() {}

 

@JsProperty public native ReferenzaDTOGWT getNodeIdDocumentoPrincipale();

@JsProperty public native void setNodeIdDocumentoPrincipale(ReferenzaDTOGWT nodeIdDocumentoPrincipale);

 

@JsProperty public native String getEscludiControlli();

 

@JsProperty public native void setEscludiControlli(String escludiControlli);

 

@JsProperty public native String getIdDocumento();

 

@JsProperty public native void setIdDocumento(String idDocumento);

 

@JsProperty public native String getModalitaFormazione();

 

@JsProperty public native void setModalitaFormazione(String modalitaFormazione);

 

@JsProperty public native String getTipologiaDocumentale();

 

@JsProperty public native void setTipologiaDocumentale(String tipologiaDocumentale);

 

@JsProperty public native String getTipologiaFlusso();

 

@JsProperty public native void setTipologiaFlusso(String tipologiaFlusso);

 

@JsProperty public native String getTipoRepertorio();

 

@JsProperty public native void setTipoRepertorio(String tipoRepertorio);

 

@JsProperty public native Date getDataRepertorioDocumento();

 

@JsProperty public native void setDataRepertorioDocumento(Date dataRepertorioDocumento);

 

@JsProperty public native String getNumeroRepertorioDocumento();

 

@JsProperty public native void setNumeroRepertorioDocumento(String numeroRepertorioDocumento);

 

@JsProperty public native String getCodiceRegistroRepertorioDocumento();

 

@JsProperty public native void setCodiceRegistroRepertorioDocumento(String codiceRegistroRepertorioDocumento);

 

@JsProperty public native Date getDataAnnullamentoRepertorioDocumento();

 

@JsProperty public native void setDataAnnullamentoRepertorioDocumento(Date dataAnnullamentoRepertorioDocumento);

 

@JsProperty public native String getMotivazioneAnnullamentoRepertorioDocumento();

 

@JsProperty public native void setMotivazioneAnnullamentoRepertorioDocumento(String motivazioneAnnullamentoRepertorioDocumento);

 

@JsProperty public native Date getDataProtocolloMittente();

 

@JsProperty public native void setDataProtocolloMittente(Date dataProtocolloMittente);

 

@JsProperty public native String getNumeroProtocolloMittente();

 

@JsProperty public native void setNumeroProtocolloMittente(String numeroProtocolloMittente);

 

@JsProperty public native String getCodiceAmministrazione();

 

@JsProperty public native void setCodiceAmministrazione(String codiceAmministrazione);

 

@JsProperty public native String getCodiceAoo();

 

@JsProperty public native void setCodiceAoo(String codiceAoo);

 

@JsProperty public native String getOggettoDocumento();

 

@JsProperty public native void setOggettoDocumento(String oggettoDocumento);

 

@JsProperty public native String getParoleChiaveDocumento();

 

@JsProperty public native void setParoleChiaveDocumento(String paroleChiaveDocumento);

 

@JsProperty public native List<SoggettoDTOGWT> getSoggettiDocumento();

 

@JsProperty public native void setSoggettiDocumento(List<SoggettoDTOGWT> soggettiDocumento);

 

@JsProperty public native String getIndiceClassificazioneDocumento();

 

@JsProperty public native void setIndiceClassificazioneDocumento(String indiceClassificazioneDocumento);

 

@JsProperty public native String getDescrizioneClassificazioneDocumento();

 

@JsProperty public native void setDescrizioneClassificazioneDocumento(String descrizioneClassificazioneDocumento);

 

@JsProperty public native String getPianoClassificazioneDocumento();

 

@JsProperty public native void setPianoClassificazioneDocumento(String pianoClassificazioneDocumento);

 

@JsProperty public native boolean isRiservato();

 

@JsProperty public native void setRiservato(boolean riservato);

 

@JsProperty public native String getFormato();

 

@JsProperty public native void setFormato(String formato);

 

@JsProperty public native String getSoftwareNome();

 

@JsProperty public native void setSoftwareNome(String softwareNome);

 

@JsProperty public native String getSoftwareVersione();

 

@JsProperty public native void setSoftwareVersione(String softwareVersione);

 

@JsProperty public native String getSoftwareProduttore();

 

@JsProperty public native void setSoftwareProduttore(String softwareProduttore);

 

@JsProperty public native boolean isFirmato();

 

@JsProperty public native void setFirmato(boolean firmato);

 

@JsProperty public native boolean isSigillatoElettronicamente();

 

@JsProperty public native void setSigillatoElettronicamente(boolean sigillatoElettronicamente);

 

@JsProperty public native boolean isMarcatoTemporalmente();

 

@JsProperty public native void setMarcatoTemporalmente(boolean marcatoTemporalmente);

 

@JsProperty public native boolean isConformita();

 

@JsProperty public native void setConformita(boolean conformita);

 

@JsProperty public native String getNoteDocumento();

 

@JsProperty public native void setNoteDocumento(String noteDocumento);

 

@JsProperty public native Integer getTempoConservazioneDocumento();

 

@JsProperty public native void setTempoConservazioneDocumento(Integer tempoConservazioneDocumento);

 

@JsProperty public native List<ReferenzaDTOGWT> getNodeIdAllegati();

 

@JsProperty public native void setNodeIdAllegati(List<ReferenzaDTOGWT> nodeIdAllegati);

 

@JsProperty public native List<ReferenzaDTOGWT> getNodeIdAggregazioni();

 

@JsProperty public native void setNodeIdAggregazioni(List<ReferenzaDTOGWT> nodeIdAggregazioni);

 

@JsProperty public native FileDTOGWT getFile();

 

@JsProperty public native void setFile(FileDTOGWT file);

@JsProperty public native ReferenzaDTOGWT getNodeIdClassificazione();

 

@JsProperty public native void setNodeIdClassificazione(ReferenzaDTOGWT nodeIdClassificazione);

 

@JsProperty public native boolean isModifica();

 

@JsProperty public native void setModifica(boolean modifica);

 

private Map<String, List<MetadatoDTOGWT>> mappaAltriMetadati;

@JsProperty public native Map<String, List<MetadatoDTOGWT>> getMappaAltriMetadati();

@JsProperty public native void setMappaAltriMetadati(Map<String, List<MetadatoDTOGWT>> mappaAltriMetadati);

 

private Map<String, String> errors;

@JsProperty public native Map<String, String> getErrors();

@JsProperty public native void setErrors(Map<String, String> errors);

@JsProperty public native boolean isDatiPresenti();

 

@JsProperty public native void setDatiPresenti(boolean datiPresenti);

 

}

 

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)

public class FileDTOGWT {

   

@JsConstructor

public FileDTOGWT() {}

   

@JsProperty public native String getHash();

@JsProperty public native void setHash(String hash);

@JsProperty public native String getAlgoritmoHash();

 

@JsProperty public native void setAlgoritmoHash(String algoritmoHash);

 

@JsProperty public native String getNomeFile();

 

@JsProperty public native void setNomeFile(String nomeFile);

 

@JsProperty public native String getMimetype();

 

@JsProperty public native void setMimetype(String mimetype);

 

@JsProperty public native String getEncoding();

 

@JsProperty public native void setEncoding(String encoding);

@JsProperty public native Object getContenuto();

 

@JsProperty public native void setContenuto(Object contenuto);

 

@JsProperty public native String getReference();

 

@JsProperty public native void setReference(String reference);

}

 

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)

public class MetadatoDTOGWT  {

@JsConstructor

public MetadatoDTOGWT() { }

@JsProperty public native String getChiave();

 

@JsProperty public native void setChiave(String chiave);

@JsProperty public native boolean isMultiplo();

 

@JsProperty public native void setMultiplo(boolean multiplo);

 

@JsProperty public native List<String> getValore_s();

 

@JsProperty public native void setValore_s(List<String> valore_s);

 

@JsProperty public native List<Date> getValore_d();

 

@JsProperty public native void setValore_d(List<Date> valore_d);

 

@JsProperty public native List<Integer> getValore_i();

 

@JsProperty public native void setValore_i(List<Integer> valore_i);

 

@JsProperty public native List<Boolean> getValore_b();

 

@JsProperty public native void setValore_b(List<Boolean> valore_b);

 

@JsProperty public native List<Double> getValore_f();

 

@JsProperty public native void setValore_f(List<Double> valore_f);

 

@JsProperty public native List<PersonaDTOGWT> getValore_p();

 

@JsProperty public native void setValore_p(List<PersonaDTOGWT> valore_p);

 

@JsProperty public native boolean getMultiplo();

 

}

 

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)

public class PersonaDTOGWT {

@JsConstructor

public PersonaDTOGWT() {}

@JsProperty public native int getType();

@JsProperty public native void setType(int type);

@JsProperty public native String getIndirizzo();

@JsProperty public native void setIndirizzo(String indirizzo);

@JsProperty public native String getCap();

@JsProperty public native void setCap(String cap);

@JsProperty public native String getCitta();

@JsProperty public native void setCitta(String citta);

@JsProperty public native String getIndirizzoDigitale();

@JsProperty public native void setIndirizzoDigitale(String indirizzoDigitale);

@JsProperty public native String getEmailNonPec();

@JsProperty public native void setEmailNonPec(String emailNonPec);

@JsProperty public native String getNumeroTelefono();

@JsProperty public native void setNumeroTelefono(String numeroTelefono);

@JsProperty public native String getContattiSocial();

@JsProperty public native void setContattiSocial(String contattiSocial);

@JsProperty public native String getCategoria();

@JsProperty public native void setCategoria(String categoria);

@JsProperty public native String getNome();

@JsProperty public native void setNome(String nome);

@JsProperty public native String getCognome() ;

@JsProperty public native void setCognome(String cognome);

@JsProperty public native String getCodiceFiscale();

@JsProperty public native void setCodiceFiscale(String codiceFiscale);

@JsProperty public native String getDenominazioneAmministrazione();

@JsProperty public native void setDenominazioneAmministrazione(String denominazioneAmministrazione);

@JsProperty public native String getCodiceIpaAmministrazione();

 

@JsProperty public native void setCodiceIpaAmministrazione(String codiceIpaAmministrazione);

 

@JsProperty public native String getDenominazioneAoo();

@JsProperty public native void setDenominazioneAoo(String denominazioneAoo);

@JsProperty public native String getCodiceIpaAoo();

@JsProperty public native void setCodiceIpaAoo(String codiceIpaAoo);

@JsProperty public native String getDenominazioneUor();

 

@JsProperty public native void setDenominazioneUor(String denominazioneUor);

@JsProperty public native String getCodiceIpaUor();

@JsProperty public native void setCodiceIpaUor(String codiceIpaUor);

@JsProperty public native String getDenominazioneOrganizzazione();

@JsProperty public native void setDenominazioneOrganizzazione(String denominazioneOrganizzazione);

@JsProperty public native String getPartitaIva();

@JsProperty public native void setPartitaIva(String partitaIva);

 

@JsProperty public native String getDenominazioneUfficio();

@JsProperty public native void setDenominazioneUfficio(String denominazioneUfficio);

@JsProperty public native String getDenominazioneSoftware();

@JsProperty public native void setDenominazioneSoftware(String denominazioneSoftware);

@JsProperty public native String getIdentificativoCartaIdentita();

@JsProperty public native void setIdentificativoCartaIdentita(String identificativoCartaIdentita);

@JsProperty public native List<String> getWebpushSubscriptions();

@JsProperty public native void setWebpushSubscriptions(List<String> webpushSubscriptions);

@JsProperty public native String getUserNameAlfresco();

@JsProperty public native void setUserNameAlfresco(String userNameAlfresco);

@JsProperty public native String getEmailAlfresco();

@JsProperty public native void setEmailAlfresco(String emailAlfresco);

@JsProperty public native String getNomeAlfresco();

@JsProperty public native void setNomeAlfresco(String nomeAlfresco);

@JsProperty public native String getCognomeAlfresco();

 

@JsProperty public native void setCognomeAlfresco(String cognomeAlfresco);

@JsProperty public native String getNodeIdUserAlfresco();

@JsProperty public native void setNodeIdUserAlfresco(String nodeIdUserAlfresco);

}

 

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)

public class ReferenzaDTOGWT {

@JsConstructor

public ReferenzaDTOGWT() {}

 

@JsProperty public native String getId();

 

@JsProperty public native void setId(String id);

 

}

 

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)

public class SoggettoDTOGWT {

@JsConstructor

public SoggettoDTOGWT() {}

@JsProperty public native String getRuolo();

@JsProperty public native void setRuolo(String ruolo);

@JsProperty public native PersonaDTOGWT getPersona();

@JsProperty public native void setPersona(PersonaDTOGWT personaDTOGWT);

}

 

 

This is the code i use (note i full the documentoPAC4DTOGWT java object with the setter and getter method:
 
 

DocumentoPAC4DTOGWT documentoPAC4DTOGWT = ...

String dtoJsonReq = JSON.stringify(documentoPAC4DTOGWT); // Here the error

GWT.log("DocumentoPAC4DTOJson: " + dtoJsonReq);

 

This is the error i get:

 

Exception caught: (TypeError) : Converting circular structure to JSON
     --> starting at object with constructor 'Ixf_g$'
     |     property 'hashCodeMap_0_g$' -> object with constructor 'XYs_g$'
     --- property 'host_2_g$' closes the circle
 

Full error on the image:
gwt_issue.png

i can not understand why i get this error ... can anyone help me to understand it ?

Thomas Broyer

unread,
May 22, 2024, 5:45:39 AMMay 22
to GWT Users
Assuming `List` and `Map` are from `java.util`, you can't just pass them to `JSON.stringify()`; you need to somehow transform them to "native" JS arrays and objects (either from the setters using a @JsOverlay method, or passing a @JsFunction to JSON.stringify() as its second argument so you could use `x instanceof List` and `x instance Map`)
Same for Date (use JsDate instead) and Object for the contenuto property.

When using JsInterop, you really need to think in terms of JavaScript, even if you're writing it using the Java syntax.

Vassilis Virvilis

unread,
May 22, 2024, 5:50:10 AMMay 22
to google-we...@googlegroups.com
or passing a @JsFunction to JSON.stringify() as its second argument

I wish I knew that some time before...
 
--
Vassilis Virvilis

Marco Tenti (IoProgrammo88)

unread,
May 22, 2024, 6:43:56 AMMay 22
to GWT Users

I misunderstood the documentation... ty for the clarification Thomas can you give me some confirmations.

The Date issue, When you say to use the JsDate do you mean the one in the elemental2 package (elemental2.core.JsDate) or in the gwt core (com.google.gwt.core.client.JsDate) ?

So for the Date issue i just enough to replace this code :

@JsProperty public native Date getDataRepertorioDocumento();

 

@JsProperty public native void setDataRepertorioDocumento(Date dataRepertorioDocumento);

 

With:

@JsProperty public native JsDate getDataRepertorioDocumento();

 

@JsProperty public native void setDataRepertorioDocumento(JsDate dataRepertorioDocumento);

 

Right ?

 

For the "List" and "Map" problem, i will probably try to use some @JsOverlay instead to use a second argument  on the JSON.stringify by the way can you point me out some example (i'm not very skilled with this library) ? 
Also I found this project updated for GWT 2.9.0 and java 11  
https://github.com/jp-solutions/gwt-interop-utilsit’s seem goof enough for my use case,  i’ll try out and let you know it. 

Thomas Broyer

unread,
May 22, 2024, 11:42:29 AMMay 22
to GWT Users
On Wednesday, May 22, 2024 at 12:43:56 PM UTC+2 tenti...@gmail.com wrote:

I misunderstood the documentation... ty for the clarification Thomas can you give me some confirmations.

The Date issue, When you say to use the JsDate do you mean the one in the elemental2 package (elemental2.core.JsDate) or in the gwt core (com.google.gwt.core.client.JsDate) ?

Any one of them, anything that directly maps to a native JS Date object.
 

So for the Date issue i just enough to replace this code :

@JsProperty public native Date getDataRepertorioDocumento();

 

@JsProperty public native void setDataRepertorioDocumento(Date dataRepertorioDocumento);

 

With:

@JsProperty public native JsDate getDataRepertorioDocumento();

 

@JsProperty public native void setDataRepertorioDocumento(JsDate dataRepertorioDocumento);

 

Right ?

Yes.
(note that it works for serializing because a JS Date object has a toJSON() method that returns its toISOString(), but it won't work for parsing JSON, for that you will have to pass a reviver function to JSON.parse() that will have to be aware of your object structure to know that the dataRepertorioDocumento property value needs to be parsed to a Date object, or use a @JsOverlay getter/setter pair that will serialize/parse the java.util.Date or JsDate value to/from the JSON representation you want, same as List and Map)

I also missed an instance of Integer in your objects, this will have to be changed to Double.
 

For the "List" and "Map" problem, i will probably try to use some @JsOverlay instead to use a second argument  on the JSON.stringify by the way can you point me out some example (i'm not very skilled with this library) ?


Could be as simple as (note that a copy is made each time the getter or setter is called):
ReferenzaDTOGWT[] nodeIdAllegatti;
// This could also use Elemental's JsArrayLike.asList()
@JsOverlay public List<ReferenzaDTOGWT> getNodeIdAllegatti() { return List.of(this.nodeIdAllegatti); }
@JsOverlay public void setNodeIdAllegatti(List<ReferenzaDTOGWT> nodeIdAllegatti) { this.nodeIdAllegatti = nodeIdAllegatti.toArray(new ReferenzaDTOGWT[nodeAllegatti.size()]); }

JsPropertyMap<String> errors;
@JsOverlay public Map<String, String> getErrors() {
  var ret = new HashMap<String, String>();
  errors.forEach(key -> ret.put(ret, errors.get(key)));
  return ret;
}
@JsOverlay public setErrors(Map<String, String> errors) {
  var obj = JsPropertyMap.<String>of();
  errors.forEach((key, value) -> obj.set(key, value));
  this.errors = obj;
}

Of course for the Map<String, List<MetadatoDTOGWT>> mappaAltriMetadati you'd have to also transform each List.

The JSON.stringify replacer could look like:
JSONType.StringifyReplacerFn replacer = (key, value) -> {
  if (value instanceof List<?>) {
    return ((List<?>) value).toArray();
  }
  if (value instanceof Map<?>) {
    var obj = JsPropertyMap.<String>of();
    ((Map<?>) value).forEach((k, v) -> obj.set(k, v));
    return obj;
  }
  if (value instanceof Date) {
    return ((Date) value).getTime(); // pass that to JsDate.create() if you prefer an ISO-formatted String rather than the timestamp
  }
  if (value instanceof Integer) {
    return ((Integer) value).doubleValue();
  }
  return value;
};
 
This is all totally untested (also note that I haven't actually written GWT code for years, this is all based on memory and the javadocs)

Also I found this project updated for GWT 2.9.0 and java 11  https://github.com/jp-solutions/gwt-interop-utilsit’s seem goof enough for my use case,  i’ll try out and let you know it.

Not sure what it actually brings on top of plain old JsInterop Base or Elemental Core…

Craig Mitchell

unread,
May 22, 2024, 9:35:03 PMMay 22
to GWT Users
Would it not be easier to just create your JSON object manually with the classes in com.google.gwt.json.client?  Ie: JSONObject, JSONArray, ...
Reply all
Reply to author
Forward
0 new messages