Vaadin Validate date, not empty - vaadin

Im trying to write validation in Vaadin but I don't understand how to check if date field is empty
I wrote something like this
#Override
public void setConfiguration(EditorConfiguration editorConfiguration) {
boolean required = ((DateFieldConfiguration) editorConfiguration).isRequired();
if (required == true) {
setRequiredIndicatorVisible(true);
addValueChangeListener(event -> validate(event.getSource().getDefaultValidator(), event.getValue()));
}
}
private void validate(Validator<LocalDate> defaultValidator, LocalDate localDate) {
binder.forField(this).withValidator(validator).asRequired("Mandatory").bind(s -> getValue(),
(b, v) -> setValue(v));
}
I have achived a validation with a text field:
String Validator code
public class VaadinStringEditor extends TextField implements HasValueComponent<String> {
/**
*
*/
private static final long serialVersionUID = 6271513226609012483L;
private Binder<String> binder;
#PostConstruct
public void init() {
setWidth("100%");
binder = new Binder<>();
}
#Override
public void initDefaults() {
setValue("");
binder.validate();
}
#Override
public void setConfiguration(EditorConfiguration editorConfiguration) {
Validator<String> validator = ((TextFieldConfiguration) editorConfiguration).getValidator();
if (validator != null) {
binder.forField(this).withValidator(validator).asRequired("Mandatory").bind(s -> getValue(),
(b, v) -> setValue(v));
}
and I valid it here:
question.setEditorConfiguration(new TextFieldConfiguration(textRequiredValidator()));
Validator:
private Validator<String> textRequiredValidator() {
return Validator.from(v -> v != null && StringUtils.trimAllWhitespace((String) v).length() != 0,
, "Not empty");
}

You should use com.vaadin.ui.DateField for LocalDate values. Have a look at the following example.
Example bean:
public class MyBean {
private LocalDate created;
public LocalDate getCreated() {
return created;
}
public void setCreated(LocalDate created) {
this.created = created;
}
}
Editor
DateField dateField = new DateField("Date selector");
binder.forField(dateField)
.bind(MyBean::getCreated, MyBean::setCreated);
If for some reason you would like to have com.vaadin.ui.TextField for editing date, then you need to set converter like this:
Binder<MyBean> binder = new Binder<>();
TextField textDateField = new TextField("Date here:");
binder.forField(textDateField)
.withNullRepresentation("")
.withConverter(new StringToLocalDateConverter())
.bind(MyBean::getCreated, MyBean::setCreated);
Converter implementation:
public class StringToLocalDateConverter implements Converter<String, LocalDate> {
#Override
public Result<LocalDate> convertToModel(String userInput, ValueContext valueContext) {
try {
return Result.ok(LocalDate.parse(userInput));
} catch (RuntimeException e) {
return Result.error("Invalid value");
}
}
#Override
public String convertToPresentation(LocalDate value, ValueContext valueContext) {
return Objects.toString(value, "");
}
}
Note that this converter does not utilise ValueContext object that contains information that should be taken into account in more complex cases. For example, user locale should be handled.

Related

Spring Integration IntegrationFlowAdapter: how to use?

At https://docs.spring.io/spring-integration/reference/html/dsl.html#java-dsl-flow-adapter I find this code example:
#Component
public class MyFlowAdapter extends IntegrationFlowAdapter {
private final AtomicBoolean invoked = new AtomicBoolean();
public Date nextExecutionTime(TriggerContext triggerContext) {
return this.invoked.getAndSet(true) ? null : new Date();
}
#Override
protected IntegrationFlowDefinition<?> buildFlow() {
return from(this::messageSource,
e -> e.poller(p -> p.trigger(this::nextExecutionTime)))
.split(this)
.transform(this)
.aggregate(a -> a.processor(this, null), null)
.enrichHeaders(Collections.singletonMap("thing1", "THING1"))
.filter(this)
.handle(this)
.channel(c -> c.queue("myFlowAdapterOutput"));
}
public String messageSource() {
return "T,H,I,N,G,2";
}
#Splitter
public String[] split(String payload) {
return StringUtils.commaDelimitedListToStringArray(payload);
}
#Transformer
public String transform(String payload) {
return payload.toLowerCase();
}
#Aggregator
public String aggregate(List<String> payloads) {
return payloads.stream().collect(Collectors.joining());
}
#Filter
public boolean filter(#Header Optional<String> thing1) {
return thing1.isPresent();
}
#ServiceActivator
public String handle(String payload, #Header String thing1) {
return payload + ":" + thing1;
}
}
Besides the fact that this code doesn't seem to compile with the latest version of Spring Integration, can anyone provide a working example how how one would use this MyFlowAdapter?

Vaadin: Bind Enum values to String in Vaadin 8

I’m working on upgrading our application vaadin version from 7.7.24 to 8.13.3. We’ve completed all the dependency issues and i’m able to start the application in locally.
We have a textbox that is showing up the Event data.
Here is the class file that i'm using:
#Entity
#Table(name = "changelog")
public class ChangelogEvent extends BaseEntity
{
#Column(name = "remote_ip")
private String remoteIp;
#Column(name = "remote_host")
private String remoteHost;
#Column(name = "event")
#Enumerated(EnumType.ORDINAL)
private ChangelogEventType eventType;
#Column(name = "entity_type")
private String entityType;
public ChangelogEvent()
{
}
public ChangelogEvent(String remoteIp, String remoteHost, ChangelogEventType eventType)
{
this.remoteIp = remoteIp;
this.remoteHost = remoteHost;
this.eventType = eventType;
}
public String getRemoteIp()
{
return remoteIp;
}
public void setRemoteIp(String remoteIp)
{
this.remoteIp = remoteIp;
}
public ChangelogEventType getEventType()
{
return eventType;
}
public void setEventType(ChangelogEventType eventType)
{
this.eventType = eventType;
}
public String getRemoteHost()
{
return remoteHost;
}
public void setRemoteHost(String remoteHost)
{
this.remoteHost = remoteHost;
}
public String getEntityType()
{
return entityType;
}
public void setEntityType(String entityType)
{
this.entityType = entityType;
}
}
And here is my ChangelogEventType.java file that defined ChangelogEventType enum:
public enum ChangelogEventType
{
CREATED("Created"),
UPDATED("Updated"),
DELETED("Deleted"),
LOGIN("Login"),
LOGOUT("Logout"),
LOGIN_RETRY("Login Retry"),
ACCOUNT_LOCKED("Account Locked"),
PASSWORD_EXPIRED("Password Expired"),
PASSWORD_CHANGED("Password Changed");
private String text;
ChangelogEventType(String text)
{
this.text = text;
}
public String getText()
{
return text;
}
public static ChangelogEventType fromString(String text)
{
if (text != null)
{
for (ChangelogEventType event : ChangelogEventType.values())
{
if (text.equalsIgnoreCase(event.text))
{
return event;
}
}
}
return null;
}
}
Here is the code that i'm using for binding the values into textfield.
eventType = createTextField("Event", COLUMN_WIDTH);
binder.forField(eventType)
.withNullRepresentation("None")
.bind(ChangelogEvent::getEventType, ChangelogEvent::setEventType);
Is there any way to bind the Enum to textbox ?
You need to write custom converter and use it in Binder using withConverter method, in your case something like:
class StringToChangelogEventTypeConverter implements Converter<String, ChangelogEventType> {
#Override
public Result<ChangelogEventType> convertToModel(String fieldValue, ValueContext context) {
// Produces a converted value or an error
ChangelogEventType event = ChangelogEventType.fromString(fieldValue);
if (event != null) {
// ok is a static helper method that creates a Result
return Result.ok(ChangelogEventType.fromString(fieldValue));
} else {
// error is a static helper method that creates a Result
return Result.error("Please enter a number");
}
}
#Override
public String convertToPresentation(ChangelogEventType event, ValueContext context) {
// Converting to the field type should always succeed,
// so there is no support for returning an error Result.
return event.getText();
}
}

Updating adapter of AutoCompleteTextView from LiveData

I have a AutoCompleteTextView that I give it 2 different adapters depending on the amount of text that is being present at the textview - if it has 0 characters I want it to display a list of "recently searched" strings adapter, while if it has more than 1 characters I want it to display auto completion list.
My getRecentlySearchedQueries method along with the RecentSearchedViewModel-
private List<String> recentlySearchedQueries = new ArrayList<>(); // pasted from the top of the class
#Override
public void getRecentlySearchedQueries() {
recentSearchViewModel.getAllQueries().observe(getActivity(), databaseRecentlySearchList -> {
if (databaseRecentlySearchList == null) {
return;
}
for (int i = 0; i < databaseRecentlySearchList.size(); i++) {
Log.d("localDBValue", "Added value - " + databaseRecentlySearchList.get(i).toString() + "\n");
String query = databaseRecentlySearchList.get(i).getQuery();
recentlySearchedQueries.add(query);
}
//Log.d("localDBValue", "recent search list value - " + recentlySearchedQueries);
});
}
public class RecentSearchViewModel extends AndroidViewModel {
private RecentSearchRepository recentSearchRepository;
private LiveData<List<RecentSearchModel>> allRecentlySearched;
public RecentSearchViewModel(#NonNull Application application) {
super(application);
recentSearchRepository = new RecentSearchRepository(application);
allRecentlySearched = recentSearchRepository.getAllRecentSearches();
}
public void insert(RecentSearchModel model) {
recentSearchRepository.insert(model);
}
public void update(RecentSearchModel model) {
// add implementation in the future if needed
}
public void delete(RecentSearchModel model) {
// add implementation in the future if needed
}
public LiveData<List<RecentSearchModel>> getAllQueries() {
return allRecentlySearched;
}
}
public class RecentSearchRepository {
private RecentSearchDao recentSearchDao;
private LiveData<List<RecentSearchModel>> allRecentSearches;
public RecentSearchRepository(Application application) {
MarketplaceDatabase database = MarketplaceDatabase.getRecentSearchInstance(application);
recentSearchDao = database.recentSearchDao();
allRecentSearches = recentSearchDao.getRecentSearchList();
}
public void insert(RecentSearchModel model) {
new RecentSearchRepository.InsertRecentSearchAsyncTask(recentSearchDao).execute(model);
}
public void update (RecentSearchModel model) {
//TODO - implement in future if needed
}
public void delete(RecentSearchModel model) {
//TODO - implement in future if needed
}
public LiveData<List<RecentSearchModel>> getAllRecentSearches() {
return allRecentSearches;
}
private static class InsertRecentSearchAsyncTask extends AsyncTask<RecentSearchModel, Void, Void> {
private RecentSearchDao recentSearchDao;
public InsertRecentSearchAsyncTask(RecentSearchDao recentSearchDao) {
this.recentSearchDao = recentSearchDao;
}
#Override
protected Void doInBackground(RecentSearchModel... recentSearchModels) {
recentSearchDao.insert(recentSearchModels[0]);
return null;
}
}
private static class UpdateRecentSearchAsyncTask extends AsyncTask<RecentSearchModel, Void, Void> {
private RecentSearchDao recentSearchDao;
public UpdateRecentSearchAsyncTask(RecentSearchDao recentSearchDao) {
this.recentSearchDao = recentSearchDao;
}
#Override
protected Void doInBackground(RecentSearchModel... recentSearchModels) {
recentSearchDao.update(recentSearchModels[0]);
return null;
}
}
}
#Dao
public interface RecentSearchDao {
#Insert()
void insert(RecentSearchModel model);
#Update
void update(RecentSearchModel model);
#Delete
void delete(RecentSearchModel model);
#Query("select * from recent_search_table")
LiveData<List<RecentSearchModel>> getRecentSearchList();
}
#Entity(tableName = "recent_search_table")
public class RecentSearchModel {
#PrimaryKey(autoGenerate = true)
private int ID;
private String query;
public RecentSearchModel(){
}
public RecentSearchModel(String query) {
this.query = query;
}
public void setID(int ID) {
this.ID = ID;
}
public int getID() {
return ID;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
#Override
public String toString() {
return "RecentSearchModel{" +
"query='" + query + '\'' +
'}';
}
#Override
public boolean equals(#Nullable Object obj) {
if (obj instanceof RecentSearchModel)
return this.query.equalsIgnoreCase(((RecentSearchModel) obj).query);
return false;
}
}
So, what I am doing here is for start getting all values inside my local DB and adding them to my String list that is part of the adapter. So far so good.
The issue I am facing is that the adapter won't show the amount of strings available in the list that populates it. In fact, it sometimes shows a view half-cut with wierd information, sometimes does not show anything and sometimes shows part of the corrent information. What am I missing?
Another thing I am facing is that the "recently searched" adapter won't work when clicking on the AutoCompleteTextView - it only works when typing and deleting values so the char length is 0. How can I make it work from start of focus?
Here is the way I am populating the information to the ViewModel -
/**
* Shows the searched products following
*/
#Override
public void getSearchedProducts(String searchedQuery) {
MarketplaceUtils.getSearchedProducts(searchedQuery, marketApiCalls, false, initialSearchTake, initialMarketplacePage, new MarketplaceUtils.OnProductsFetchCompleteListener() {
#Override
public void onSuccess(List<MiniProductModel> list) {
if (!searchedQuery.equals(currentSearchedText))
return;
if (list == null) {
//reaching here means we do not have a result to show to the UI so we empty the list.
currentProductList.clear();
productsAdapter.notifyDataSetChanged();
return;
}
if (searchedQuery.length() > 3 && searchAutoCompleteStrings.contains(searchedQuery)) {
Log.d("localDBValue", "searchedValue - " + searchedQuery);
recentSearchViewModel.insert(new RecentSearchModel(searchedQuery));
}
mPresenter.setDiscoverProductsLayoutVisibility(View.GONE);
currentProductList.clear();
currentProductList.addAll(list);
productsAdapter.notifyDataSetChanged();
}
#Override
public void onError(Throwable throwable) {
Log.d("searchedProducts", throwable.getMessage());
}
});
}
The default behaviour for #Insert method of Room is OnConflictStrategy.ABORT - so what I did is to implement equals() method to verify that the RecentSearchModels that are being compared are compared by their string value. Still does seems to effect anything.

button inside column for each row in tableview

In my TableView I have column with button for each row for update so I need when click the button to take all the values from the row to a new fxml window
This is my contractor class:
public class constractor {
private String co_id;
private String co_name;
private String co_address;
private String co_create_date;
private String co_description;
private String co_mobile;
private String co_type_compile;
private String co_status;
private String co_type_model;
private Button button;
public constractor(String co_id, String co_name, String co_type_compile, String co_description, String co_create_date, String co_status, String co_address, String co_mobile, String co_type_model, String button) {
this.co_id = co_id;
this.co_name = co_name;
this.co_type_compile = co_type_compile;
this.co_description = co_description;
this.co_create_date = co_create_date;
this.co_status = co_status;
this.co_address = co_address;
this.co_mobile = co_mobile;
this.co_type_model = co_type_model;
this.button = new Button("edit");
}
public String getCo_id() {
return co_id;
}
public void setCo_id(String co_id) {
this.co_id = co_id;
}
public String getCo_name() {
return co_name;
}
public void setCo_name(String co_name) {
this.co_name = co_name;
}
public String getCo_address() {
return co_address;
}
public void setCo_address(String co_address) {
this.co_address = co_address;
}
public String getCo_create_date() {
return co_create_date;
}
public void setCo_create_date(String co_create_date) {
this.co_create_date = co_create_date;
}
public String getCo_description() {
return co_description;
}
public void setCo_description(String co_description) {
this.co_description = co_description;
}
public String getCo_mobile() {
return co_mobile;
}
public void setCo_mobile(String co_mobile) {
this.co_mobile = co_mobile;
}
public String getCo_type_compile() {
return co_type_compile;
}
public void setCo_type_compile(String co_type_compile) {
this.co_type_compile = co_type_compile;
}
public String getCo_status() {
return co_status;
}
public void setCo_status(String co_status) {
this.co_status = co_status;
}
public String getCo_type_model() {
return co_type_model;
}
public void setCo_type_model(String co_type_model) {
this.co_type_model = co_type_model;
}
public Button getButton() {
return button;
}
public void setButton(Button button) {
this.button = button;
}
}
This is my class for table:
public class MainscreenController implements Initializable {
#FXML
private TableView<constractor> co_tableview;
#FXML
private TableColumn<constractor, String> col_id;
#FXML
private TableColumn<constractor, String> col_name;
#FXML
private TableColumn<constractor, String> col_compaile_type;
#FXML
private TableColumn<constractor, String> col_description;
#FXML
private TableColumn<constractor, String> col_ceartedat;
#FXML
public TableColumn<constractor, String> col_status;
#FXML
private TableColumn<constractor, String> col_mobile;
#FXML
private TableColumn<constractor, String> col_type_model;
#FXML
private TextField search;
#FXML
private TableColumn<constractor, Button> col_button;
int indexorder = -1;
ObservableList<constractor> orderdata = FXCollections.observableArrayList();
#FXML
public void ordertables() {
Connection con = DB.getConnection();
orderdata.clear();
try {
try (ResultSet rs = con.createStatement().executeQuery("select * from mr_order")) {
while (rs.next()) {
orderdata.add(new constractor(
rs.getString("co_id"),
rs.getString("co_name"),
rs.getString("co_type_model"),
rs.getString("co_description"),
rs.getString("co_create_date"),
rs.getString("co_status"),
rs.getString("co_mobile"),
rs.getString("co_address"),
rs.getString("co_type_compile"),
rs.getString("co_user_id")
));
}
countneworder();
}
} catch (SQLException ex) {
Logger.getLogger(MainscreenController.class.getName()).log(Level.SEVERE, null, ex);
}
}
public int tablesandsearchorder() {
////tableview Itemsinserting
col_id.setCellValueFactory(new PropertyValueFactory<>("co_id"));
col_name.setCellValueFactory(new PropertyValueFactory<>("co_name"));
col_compaile_type.setCellValueFactory(new PropertyValueFactory<>
("co_type_compile"));
col_description.setCellValueFactory(new PropertyValueFactory<>
("co_description"));
col_ceartedat.setCellValueFactory(new PropertyValueFactory<>
("co_create_date"));
col_status.setCellValueFactory(new PropertyValueFactory<>("co_status"));
col_mobile.setCellValueFactory(new PropertyValueFactory<>("co_mobile"));
col_type_model.setCellValueFactory(new PropertyValueFactory<>
("co_type_model"));
col_button.setCellValueFactory(new PropertyValueFactory<>("button"));
co_tableview.setItems(orderdata);
co_tableview.getItems().setAll(orderdata);
co_tableview.itemsProperty().addListener((observable, oldItems, newItems)
-> {
countorder.textProperty().bind(
Bindings.size(newItems).asString());
});
// 2. Set the filter Predicate whenever the filter changes.
search.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
filteredData.setPredicate(constractor -> {
// If filter text is empty, display all persons.
if (newValue == null || newValue.isEmpty()) {
return true;
}
// Compare first name and last name of every person with filter text.
String lowerCaseFilter = newValue.toLowerCase();
if
(constractor.getCo_name().toLowerCase().contains(lowerCaseFilter)) {
return true; // Filter matches first name.
} else if (constractor.getCo_id().toLowerCase().contains(lowerCaseFilter)) {
return true; // Filter matches last name.
} else if
(constractor.getCo_description().toLowerCase().contains(lowerCaseFilter)) {
return true; // Filter matches last name.
}
return false; // Does not match.
});
});
// 3. Wrap the FilteredList in a SortedList.
SortedList<constractor> sortedData = new SortedList<>(filteredData);
// 4. Bind the SortedList comparator to the TableView comparator.
sortedData.comparatorProperty().bind(co_tableview.comparatorProperty());
// 5. Add sorted (and filtered) data to the table.
co_tableview.setItems(sortedData);
return 0;
}
#FXML
public void openinsert() {
try {
//in this fxml i create the new order and also i need for update the status the order from this fxml when i click the button inside the tableview
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("createorder.fxml"));
Scene scene = new Scene(fxmlLoader.load());
Stage stage = new Stage();
stage.setTitle("neworder");
stage.setScene(scene);
stage.setFullScreen(false);
stage.setResizable(false);
stage.setMinHeight(400);
stage.setMinWidth(600);
stage.show();
} catch (IOException e) {
Logger logger = Logger.getLogger(getClass().getName());
logger.log(Level.SEVERE, "Failed to create new Window.", e);
}
}
It's usually recommended not mixing the view code (Button) with the model code (constractor). Instead you should use a custom TableCell class for the column.
Assuming you know how to pass the data (otherwise take a look here: Passing Parameters JavaFX FXML), all required info should be available via the constractor instance which you should pass to the new scene.
MainscreenController
#FXML
private TableColumn<constractor, Void> col_button;
...
private void editConstractor(constractor constractor) {
// TODO: implement
}
#FXML
private void initialize() {
col_button.setCellFactory(col -> new TableCell<constractor, Void>() {
private final Button button;
{
button = new Button("edit");
button.setOnAction(evt -> {
constractor item = getTableRow().getItem();
editConstractor(item);
});
}
#Override
protected void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : button);
}
});
}
You also need to remove the cellValueFactory for the button column.
Note:
Sticking to the java naming conventions would make the code easier to read. (Type names should start with an uppercase letter and identifiers should use camelCase instead of underscores assuming they're not for a static final field.)
constractor is most likely misspelled. Did you mean contractor? I recommend using the renaming functionality of your IDE to fix this typo...
(In my code I used the same spelling for the editConstractor method.)

Adapt field to store to database

Say I have a field content that is a json. I would like to store it in database so that my domain class keeps only the 1 field only. (It's more of a brain task ;-)
class MyDomain{
def content
static constraints = {
content nullable: false, blank: false, sqlType: "text" // adapter from JSON to String??
}
def beforeInsert(){
content = content.toString()
}
def beforeUpdate(){
content = content.toString()
}
def afterInsert(){
content = JSON.parse(content) as JSON
}
def afterUpdate(){
content = JSON.parse(content) as JSON
}
def onLoad(){
content = JSON.parse(content) as JSON
}
}
I want my domain object to expose only content so I don't want to use another field like String contentAsText because it would be visible outside.
In the whole GORM documentation I haven't found a thing how to manage it. I've tried beforeValidate()/beforeInsert() and onLoad() methods but no luck...
How can I adapt the value before it gets persisted?
You can define a custom hibernate user-type for JSONElement as described here: https://stackoverflow.com/a/28655708/607038
In domain class constraints:
static constraints = {
content type: JSONObjectUserType
}
User Type Class:
import org.grails.web.json.JSONObject
import org.hibernate.HibernateException
import org.hibernate.engine.spi.SessionImplementor
import org.hibernate.type.StandardBasicTypes
import org.hibernate.usertype.EnhancedUserType
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
import java.sql.Types
class JSONObjectUserType implements EnhancedUserType, Serializable {
private static final int[] SQL_TYPES = [Types.VARCHAR]
#Override
public int[] sqlTypes() {
return SQL_TYPES
}
#Override
public Class returnedClass() {
return JSONObject.class
}
#Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true
}
if (x == null || y == null) {
return false
}
JSONObject zx = (JSONObject) x
JSONObject zy = (JSONObject) y
return zx.equals(zy)
}
#Override
public int hashCode(Object object) throws HibernateException {
return object.hashCode()
}
#Override
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
Object jsonObject = StandardBasicTypes.STRING.nullSafeGet(resultSet, names, session, owner)
if (jsonObject == null) {
return null
}
return new JSONObject((String) jsonObject)
}
#Override
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
if (value == null) {
StandardBasicTypes.STRING.nullSafeSet(preparedStatement, null, index, session)
} else {
JSONObject jsonObject = (JSONObject) value
StandardBasicTypes.STRING.nullSafeSet(preparedStatement, jsonObject.toString(), index, session)
}
}
#Override
public Object deepCopy(Object value) throws HibernateException {
return value
}
#Override
public boolean isMutable() {
return false
}
#Override
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value
}
#Override
public Object assemble(Serializable cached, Object value) throws HibernateException {
return cached
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original
}
#Override
public String objectToSQLString(Object object) {
throw new UnsupportedOperationException()
}
#Override
public String toXMLString(Object object) {
return object.toString()
}
#Override
public Object fromXMLString(String string) {
return new JSONObject(string)
}
}
class MyDomain{
JSONElement content
static constraints = {
content nullable: false, blank: false, sqlType: "text" // adapter from Map to String??
}
def setContent(String textContent){
content = JSON.parse(textContent)
}
}
I had to do 2 things.
replace def content with JSON content so that it gets persisted, see Grails Domain Constructor is not Groovy Constructor
Convert a json string back to json via def setContent().
As content is JSONElement use JSONObject and JSONArray as concrete classes.

Resources