Alguém consegue me dar um exemplo de serialização com Gson que funcione?

Olá. Eu tentei serializar um objeto recentemente, porém dentro do mesmo existem várias StringProperty, e as mesmas não são serializáveis. Então eu descobri que é possível serializar com Gson, porém eu estou a dois dias testando exemplos na internet todos dão em exceções diferentes. Alguém pode me dar um exemplo? Eu tenho somente uma classe User com várias StringProperty e quero salvar em um .json.

Um palpite maroto: Não seria melhor desacoplar tua camada de negócio (ou de modelo) da camada de UI?

Pelo que eu vi aqui, a StringProperty é uma classe do JavaFX, correto? Acredito que se você precisa serializar a entidade, provavelmente é uma entidade do negócio independente da UI.

Se você fizer essa separação, vai ficar simples serializar o POJO. Agora, pode também ser o caso de você precisar utilizar as funcionalidades da StringProperty de alguma forma amarrada com os outros dados do POJO.

Se realmente você não pode realizar a separação, você sabe como é que o GSON serializa teus objetos? Se tiver como configurar ele para utilizar os GETTERS ao invés da propriedades em si, você pode também fazer algo como:

public String getStringProperty() {
    String foo = ...; // Transforma a StringProperty em String
    return foo;
}

Dessa forma, na hora de remontar o objeto, você pode fazer o processo contrário para transformar a String em StringProperty.

Obs: alguns chamam isso de gambiarra, mas podemos chamar de recurso técnico.

Acredito que um exemplo real do que você precisa seria melhor !!! Poderia então colocar um código ou um exemplo aproximado da sua duvida?

Não, eu não posso separar o model da view porque existem vários binds com o objeto User.

Entendi. Tenta fazer a questão do getter/setter para serializar como String então, caso não encontre como serializar o StringProperty mesmo.

Nesse post do StackOverflow, o cara disse que conseguiu fazer um esquema com um LinkedHashMap. Já tentou algo assim?

Eu não postei o código por dois motivos, primeiro, não achei necessário pois eu quero somente serializar um objetos com atributos Property, nada mais, e segundo, minha classe User tá toda bagunçada porque eu fiquei fazendo testes que não deram certo, mas se é necessário mesmo, eu coloco aqui:

Classe User

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class User implements JsonSerializer<User> {

	private StringProperty Name = new SimpleStringProperty();
	private StringProperty Email = new SimpleStringProperty();
	private BooleanProperty Gender = new SimpleBooleanProperty();
	private StringProperty Picture = new SimpleStringProperty();
	private StringProperty googleId = new SimpleStringProperty();

	private ArrayList<String> Likes = new ArrayList<String>();
	private ArrayList<Event> Events = new ArrayList<Event>();

	public User(String googleId2, String n, String em, boolean g, String p, ArrayList<String> l) {
		this.Name.set(formatName(n));
		this.Email.set(em);
		this.Gender.set(g);
		this.Picture.set(p);
		this.Likes = l;
		this.googleId.set(googleId2);;
	}
	
	public User(){
		
	}
	
	private String formatName(String n){
		n = n.replaceFirst(" ", "");
		String r = "";
		String[] split = n.split(" ");
		for(String str : split){
			str = str.substring(0,1).toUpperCase().concat(str.substring(1));
			r+=str;
		}
		return r;
	}

	public final StringProperty NameProperty() {
		return this.Name;
	}
	

	public final String getName() {
		return this.NameProperty().get();
	}
	

	public final void setName(final String Name) {
		this.NameProperty().set(Name);
	}
	

	public final StringProperty emailProperty() {
		return this.Email;
	}
	

	public final String getEmail() {
		return this.emailProperty().get();
	}
	

	public final void setEmail(final String email) {
		this.emailProperty().set(email);
	}
	

	public final BooleanProperty GenderProperty() {
		return this.Gender;
	}
	

	public final boolean isGender() {
		return this.GenderProperty().get();
	}
	

	public final void setGender(final boolean Gender) {
		this.GenderProperty().set(Gender);
	}
	

	public final StringProperty PictureProperty() {
		return this.Picture;
	}
	

	public final String getPicture() {
		return this.PictureProperty().get();
	}
	

	public final void setPicture(final String Picture) {
		this.PictureProperty().set(Picture);
	}
	
	public ArrayList<String> getLikes() {
		return Likes;
	}

	public void setLikes(ArrayList<String> likes) {
		Likes = likes;
	}

	public ArrayList<Event> getEvents() {
		return Events;
	}

	public void setEvents(ArrayList<Event> events) {
		this.Events = events;
	}

	@Override
	public JsonElement serialize(User u, Type t, JsonSerializationContext j) {
        JsonObject result = new JsonObject();
        result.add("googleid", new JsonPrimitive(u.googleId.get()));
        result.add("name", new JsonPrimitive(u.NameProperty().get()));
        result.add("email", new JsonPrimitive(u.emailProperty().get()));
        result.add("gender", new JsonPrimitive(u.GenderProperty().get()));
        result.add("picture", new JsonPrimitive(u.PictureProperty().get()));
        return result;
	}
	
	public LinkedHashMap<String, Object> getUserData() {
	    LinkedHashMap<String, Object> userData = new LinkedHashMap<>();

	    userData.put("googleid", googleIdProperty().getValue());
	    userData.put("name", NameProperty().getValue());
	    userData.put("email", emailProperty().getValue());
	    userData.put("gender", GenderProperty().getValue());
	    userData.put("picture", PictureProperty().getValue());
	    return userData;
	}

	public final StringProperty googleIdProperty() {
		return this.googleId;
	}
	

	public final String getGoogleId() {
		return this.googleIdProperty().get();
	}
	

	public final void setGoogleId(final String googleId) {
		this.googleIdProperty().set(googleId);
	}	
}

Então na hora de escrever eu faço isso:

com.google.gson.Gson gson = new GsonBuilder().create();
Writer.WriteJSONObject(gson.toJson(currentUser), Methods.MakeProgramFolder().getAbsolutePath()+"/user.json");

Obs: MakeProgramFolder retorna um objeto File e o objeto currentUser é um objeto User

Writer.WriteJSONObject:

	public static boolean WriteJSONObject(String obj, String file){
	FileOutputStream FOS;
	try {
		FOS = new FileOutputStream(file);
		FOS.write(obj.getBytes());
		FOS.flush();
		FOS.close();
	} catch (IOException e) {
		e.printStackTrace();
		return false;
	}
	return true;
}

E para ler eu faço assim:

Gson gsonParser = new Gson();
Map<String, User> gsonResponse; // You don't need to do new here
Type collectionType = new TypeToken<Map<String, User>>() {}.getType();
gsonResponse = gsonParser.fromJson(Writer.ReadObject(new File(Methods.MakeProgramFolder().getAbsoluteFile()+"/user.json")), collectionType);

Writer.ReadObject

	public static String ReadObject(File file) throws IOException, ClassNotFoundException{
	return new String(Files.readAllBytes(file.toPath()));
}

E eu estou recebendo a seguinte exceção:

    Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 398 path $.
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224)
	at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
	at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:187)
	at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
	at com.google.gson.Gson.fromJson(Gson.java:887)
	at com.google.gson.Gson.fromJson(Gson.java:852)
	at com.google.gson.Gson.fromJson(Gson.java:801)
	at com.tkfentreteniment.meusdados.start.Start.main(Start.java:31)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 398 path $.
	at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:213)
	... 7 more

E o user.json ficou todo zoado:

{"Name":{"name":"","value":"ThallyssonKlein","valid":false},"Email":{"name":"","value":" thallyssonklein@gmail.com","valid":false},"Gender":{"name":"","value":false,"valid":true},"Picture":{"name":"","value":" https://lh5.googleusercontent.com/-R5JjRw7ocHs/AAAAAAAAAAI/AAAAAAAAIV8/jmrc9URk6fM/photo.jpg","valid":false},"googleId":{"name":"","value":"117892930171106636776","valid":true},"Events":[]}

Na realidade, se fosse possível a serialização normal mesmo com as Properties, eu prefiro, porém se não for possível, eu só quero gerar um .json com os atributos da classe User.

É muito simples serializar usando Gson.

import com.google.gson.Gson;

String mensagem = null;
User user = new User();
user.setName(“Douglas”);
user.setEndereco(“Rua tal, 123123”);

Gson gson = new Gson();
mensagem = gson.toJson(user);
System.out.println(mensagem);

E pra desserializar?

import com.google.gson.Gson;

//body é array de byte da mensagem
String message = new String(body, “UTF-8”);
Gson gson = new Gson();
Position position = gson.fromJson(message, Position.class);
System.out.println(message);

Eu salvei assim:

String json = null;
Gson gson = new Gson();
json = gson.toJson(currentUser);
System.out.println(json);
File fJson = new File(Methods.MakeProgramFolder().getAbsolutePath()+"/user.json");
if(!fJson.exists()){fJson.createNewFile();}
    BufferedWriter buffWrite = new BufferedWriter(new FileWriter(fJson));
buffWrite.append(json);
    buffWrite.close();

E estou lendo assim:

File fJson = new File(Methods.MakeProgramFolder().getAbsolutePath()+"/user.json");
if(!fJson.exists()){fJson.createNewFile();}
BufferedReader br = new BufferedReader(new FileReader(fJson));
String line;
String json = "";
while((line = br.readLine()) !=null){
    json+=line;
}
String u = new String(json.getBytes(), "UTF-8");
Gson gson = new Gson();
User user = gson.fromJson(u, User.class);
System.out.println(u);
//UserDAO.signInOffline(user);

E estou recebendo esta exceção:

Exception in thread "main" java.lang.RuntimeException: Failed to invoke public javafx.beans.property.StringProperty() with no args
	at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:111)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:210)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:129)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220)
	at com.google.gson.Gson.fromJson(Gson.java:887)
	at com.google.gson.Gson.fromJson(Gson.java:852)
	at com.google.gson.Gson.fromJson(Gson.java:801)
	at com.google.gson.Gson.fromJson(Gson.java:773)
	at com.tkfentreteniment.meusdados.start.Start.main(Start.java:36)
Caused by: java.lang.InstantiationException
	at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(Unknown Source)
	at java.lang.reflect.Constructor.newInstance(Unknown Source)
	at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:108)
	... 8 more

Ele está com problema na escrita ou leitura?
O que tem nessa linha?

at com.tkfentreteniment.meusdados.start.Start.main(Start.java:36)

User user = gson.fromJson(u, User.class);

Se for bindings javafx que está falando, talvez você deveria renomear o objeto User para UserView.

Ele está gerando o json?
Leitura e escrita são projetos diferentes? Estão em pastas diferentes?

A leitura e a escrita eu realizo a partir de classes diferentes. O caminho está correto, e o .json fica assim:

{"Name":{"name":"","value":"ThallyssonKlein","valid":false},"Email":{"name":"","value":" thallyssonklein@gmail.com","valid":false},"Gender":{"name":"","value":false,"valid":true},"Picture":{"name":"","value":" https://lh5.googleusercontent.com/-R5JjRw7ocHs/AAAAAAAAAAI/AAAAAAAAIV8/jmrc9URk6fM/photo.jpg","valid":false},"googleId":{"name":"","value":"117892930171106636776","valid":true},"Events":[]}

As classes precisam ser idênticas