how can I override the toString method in openedge? - openedge

I have a serializable class that I would like to provide my own toString when being serialized to JSON.
DEFINE PUBLIC PROPERTY address1 AS CHARACTER NO-UNDO
GET.
SET.
METHOD PUBLIC OVERRIDE CHARACTER toString( ):
DEFINE VARIABLE result AS CHARACTER NO-UNDO.
RETURN address1 + address2 + city + country.
END METHOD.
END CLASS. ```
I am also assigning the class to a temptable and using the write-json method of a dataset to output but I get the standard toString representation .."myClass": {
"prods:objId": 1,
"myClass": {
"address1": "xxxxx"
}
}
can I somehow override the toString being used ?

The JsonSerializer does not use ToString() ,nor does it give you any control over the format that's produced. The Serialize method describes what data is written. If you want this ability added into the ABL, you can add an "Idea" at https://openedge.ideas.aha.io/ideas ; OE product management review these ideas periodically.
If you want control today over what is written, you will need to roll your own. By way of example, OE has the IJsonSerializer interface, which allows types to declare that they can be serialised using the JsonSerializer class.

Related

Provide a Converter for data-binding by defining a pair of SerializableFunction objects

In Vaadin 8 Framework, and Vaadin 10 Flow, the data-binding capability lets us provide a Converter to mediate between the widget’s expected data type (such as String for a TextField) and the data type of the backing bean property (such as Integer number).
In this example, the built-in Converter implementation StringToIntegerConverter is used.
binder
.forField( this.phaseField )
.withConverter(
new StringToIntegerConverter( "Must enter an integer number" )
)
.bind( Panel::getPhase , Panel::setPhase ) ;
But what about defining a Converter for other types? How can I easily define a short-and-sweet Converter? For example, a String-to-UUID converter. I want to show the canonical 36-character hex string in a TextField, and going the other direction, parse that string back into a UUID.
// String to UUID
UUID uuid = UUID.fromString( myString ) ;
// UUID to String
String myString = uuid.toString() ;
I see that Binder.BindingBuilder offers the pair of methods withConverter that both take a pair of SerializableFunction objects.
Binder.BindingBuilder::withConverter(SerializableFunction<TARGET,NEWTARGET> toModel, SerializableFunction<NEWTARGET,TARGET> toPresentation)
Binder.BindingBuilder::withConverter(SerializableFunction<TARGET,NEWTARGET> toModel, SerializableFunction<NEWTARGET,TARGET> toPresentation, String errorMessage)
➥ So how do I define the pair of SerializableFunction objects/classes?
I noticed that this interface lists a known subinterface ValueProvider<SOURCE,TARGET>. That looks familiar, and I have a hunch it is the key to easily defining a short simple converter. But I do not quite comprehend the syntax with lambdas and all that is going on here.
I am not asking how to write a class implementing Converter. I am asking how to write the pair of SerializableFunction arguments to pass to the Binder.BindingBuilder::withConverter methods listed above as bullet items.
Quoting that JavaDoc:
Interface Binder.BindingBuilder<BEAN,TARGET>
…
withConverter
default <NEWTARGET> Binder.BindingBuilder<BEAN,NEWTARGET> withConverter(SerializableFunction<TARGET,NEWTARGET> toModel, SerializableFunction<NEWTARGET,TARGET> toPresentation)
Maps the binding to another data type using the mapping functions and a possible exception as the error message.
The mapping functions are used to convert between a presentation type, which must match the current target data type of the binding, and a model type, which can be any data type and becomes the new target type of the binding. When invoking bind(ValueProvider, Setter), the target type of the binding must match the getter/setter types.
For instance, a TextField can be bound to an integer-typed property using appropriate functions such as: withConverter(Integer::valueOf, String::valueOf);
Type Parameters:
NEWTARGET - the type to convert to
Parameters:
toModel - the function which can convert from the old target type to the new target type
toPresentation - the function which can convert from the new target type to the old target type
Returns:
a new binding with the appropriate type
Throws:
IllegalStateException - if bind has already been called
You can do it by passing two lambda expressions to withConverter, so something like this:
binder.forField(textField)
.withConverter(text -> UUID.fromString(text), uuid -> uuid.toString())
.bind(/* ... */);
If you need a more complicated conversion, then the right-hand side of the lambda can be surrounded with brackets, e.g.
binder.forField(textField).withConverter( text -> {
if ( text == null ) {
return something;
} else {
return somethingElse;
}
}, uuid -> { return uuid.toString(); } )
.bind(/* ... */);
If you need your converter multiple times, I recommend creating a separate class implementing interface com.vaadin.data.Converter. However, using lambdas is possible, too, as you already know (see answer of #ollitietavainen). But this is not Vaadin specific, it's a Java 8+ feature you can read about e.g. here. Basically, you can use lambdas whereever an object implementing an interface with only one method is required.

In a C# TBB: how to split a multi SingleLineTextField into seperate strings

I have a plain textfield in Tridion that can have multiple values. The itemtype is a SingleLineTextField.
In the TBB code I have the following (removed the non-essential parts):
ItemFields itemFields = new ItemFields(folder.Metadata, folder.MetadataSchema);
foreach (ItemField itemField in itemFields)
{
string itemFieldValue = string.Empty;
switch (Utilities.GetFieldType(itemField))
{
case FieldType.SingleLineTextField:
itemFieldValue = itemField.ToString();
break;
}
}
Now the result in case of two entries is just two strings with a character line break in it.
String A
String B
The method used is a generic one, which also works on other fields, so I was looking for some way to find out if a SingleLineTextField has more values in it.
You can cast the field to a SingleLineTextField type, then iterate through the Values collection, something along these lines:
SingleLineTextField field = (SingleLineTextField)itemField;
foreach(string value in field.Values)
{
// do something with value
}
// or if all you want is the count of values
int i = field.Values.Count;
Firstly, I would advise against relying on the ToString() method on objects unless it is specifically documented. In this case it works with the abstract class ItemField, but this may not always be the case.
The TOM.Net API only defines Definition and Name properties for ItemField, so you need to cast your ItemField object to something more specific.
the TextField abstract class, which SingleLineTextField inherits from, defines a ToString() method, but also Value and Values properties, which are much better suited to what you're trying to do. Looking at the documentation, we can see that Values will give us an IList<String> of the values, even if your field is not multi-valued. Perfect!
So, to answer your question, "I was looking for some way to find out if a SingleLineTextField has more values in it", you need to cast your ItemField as a TextField and check the number of Values it provides, thus:
TextField textField = (TextField)itemField;
// If you need to deal with multi-valued fields separately
if (textField.Values.Count > 1)
{
//Logic to deal with multiple values goes here
}
else
{
//Logic to deal with single valued goes here
}
// Much better... If you can deal with any number of values in a generic fashion
foreach (string value in textField.Values)
{
// Generic code goes here
}

Can I cast a string object passed on command line argument to the actual object?

Is it possible to cast a command-line passed string object back to actual object?
I want to do the following, but throwing error can't cast.
Button objPro = (Button) sender;
cProduct cp = (cProduct) objPro.CommandArgument;
If no, then why?
This is what the string holds.
cProduct cpObj = (cProduct)e.Row.DataItem;
Button btnAddProduct = (Button)e.Row.FindControl("btnAddProduct");
if (btnAddProduct != null)
{
btnAddProduct.CommandArgument = cpObj.ToString();
}
You probably can't, because it's a string. It's not a cProduct (whatever that is - consider following .NET naming conventions and naming it Product instead).
Now you could do this if you had a explicit conversion operator in cProduct to create an instance from a string.
You haven't really explained what's in the string, or what's in the type - but if your cProduct type provides a ToString method which contains all the data in a reversible form, then you could easily write a method or a constructor to create the product again:
Product product = new Product(objPro.CommandArgument);
or maybe:
Product product = Product.Parse(objPro.CommandArgument);
You'll have to write that constructor/method, of course.
I would strongly recommend using a constructor or method instead of an operator, just to keep your code clearer - it's very rarely a good idea to write your own conversion operators.
Take a look at CommandArgument on MSDN. The property is a string, when you assign the a value to the property, you aren't casting some complex type to string, you are setting a string value on the property. Can you cast a string back to your object type anyway, regardless of it being a CommandArgument. I doubt it. If the argument is an int you could try int.Parse or similar for other types which have a parse method.

DataMember Emit Default Value

I have a .Net Web Service function that can accept one string.
That function will then serialize that string to JSON, but I only want to serialize it if it's value is not "".
I found these instructions:
http://msdn.microsoft.com/en-us/library/aa347792.aspx
[DataContract]
public class MyClass
{
[DataMember (EmitDefaultValue=false)]
public string myValue = ""
}
Unfortunatelly I can not hide the myValue from the serialization because "" is not the .Net default value for a string (how dumb is that!)
One of two option ocurred
On the web service have some kind of attribute that sets the "" to null
Have some condition on the class
I would prefer the 1st because it makes the code cleaner but an opinion would be great.
Thanks
You can explicitly set what the default value is (for the purposes of serialization) using the DefaultValueAttribute class:
[DataContract]
public class MyClass
{
[DataMember (EmitDefaultValue=false)]
[DefaultValue("")]
public string myValue = ""
}
I think you have at least a couple of options here. It's extra work but worth it.
You can encapsulate the string in a reference type. Since reference types are null if not present, that lets you know right away if a string was present or not (because the encapsulating reference type would be either non-null or null, if the string is non-empty or not.)
A final option you have is to add an extra complementary variable (perhaps a boolean) that is set on OnDeserializing/OnDeserialized/OnSerializing/OnSerialized and use this to track whether or not something was actually present on the wire. You might, for example, set this complementary variable to true only when you're actually serializing out a non-empty string and similarly

What are good ways in which to specify custom currency, number and datetime formats in GWT?

I have a GWT project in which I need to manually specify currency, number and datetime formats. These customizations include specifying the currency symbols, grouping seperator, decimal seperator, negative number formats etc. What would be the best way to accomplish this?
Should I use the GWT NumberFormat class? NumberFormat makes extensive use of the GWT internationalization constructs like the Constants interface etc. If you therefore specify a custom number format mask, it will still look at the current locale and use those monetary symbols, decimal symol and thousand separators, as is specified in the late-bound and internationalized 'NumberFormat' instance.
My question is: how would you accomplish this? Would you re-implement NumberFormat's functionality? Would you derive from it and use the protected constructor and pass it in some kind of custom NumberConstants instance that you created yourself? How about getting the i18n NumberConstants instance, and using that to populate your own instance and override only what you want?
How would you approach this problem?
I have used NumberFormat.getFormat(String format) for custom currency formatting (removing "US" before the $ sign).
In my application I put the result into public constant. I have stored the "format" argument in i18n resource bundle as well as all UI specific strings.
public class MyNumberFormat extends NumberFormat{
private static CurrencyCodeMapConstants currencyCodeMapConstants = GWT.create(CurrencyCodeMapConstants.class);
protected MyNumberFormat(String pattern, CurrencyData cdata,
boolean userSuppliedPattern) {
super(pattern, cdata, userSuppliedPattern);
}
public static NumberFormat getCurrencyFormat(String currencyCode) {
return new MyNumberFormat(defaultNumberConstants.currencyPattern(),
lookupCurrency(currencyCode), false);
}
private static CurrencyData lookupCurrency(String currencyCode) {
CurrencyData currencyData = CurrencyList.get().lookup(currencyCode);
Map currencyMap = currencyCodeMapConstants.currencyMap();
String code = currencyData.getCurrencyCode();
//String symbol = currencyData.getCurrencySymbol();
String symbol = currencyMap.get(currencyCode);
int fractionDigits = currencyData.getDefaultFractionDigits();
String portableSymbol = currencyData.getPortableCurrencySymbol();
return toCurrencyData(code, symbol, fractionDigits, portableSymbol);
}
public static native CurrencyData toCurrencyData(String code, String symbol, int fractionDigits, String portableSymbol) /*-{
return [ code, symbol, fractionDigits, portableSymbol ];
}-*/;
}
I could use in GXT grid
column = new ColumnConfig("precioventa", constants.modeloPrendaPrecioVenta(), 100);
column.setAlignment(HorizontalAlignment.RIGHT);
column.setNumberFormat(MyNumberFormat.getCurrencyFormat("PEN"));
columns.add(column);

Resources