NodaPatternConverter for Instant with numeric (unix) format in 2.x.x - nodatime

As I can read on https://nodatime.org/2.0.x/userguide/migration-to-2 the support for numeric formatting of Instants has been removed.
Is there currently a way to create a NodaPatternConverter that will convert to a unix / ticks format and back?
SystemClock.Instance.GetCurrentInstant().ToString( "d", CultureInfo.InvariantCulture );
Results in the following exception:
NodaTime.Text.InvalidPatternException : The standard format "d" is not valid for the NodaTime.Instant type.
The solution based on Jon's suggestion I've ended up implementing:
public class InstantUnixTicksConverter : NodaConverterBase<Instant>
{
protected override Instant ReadJsonImpl( JsonReader reader, JsonSerializer serializer )
{
string text = reader.Value.ToString();
if ( !long.TryParse( text, out var ticks ) )
{
throw new InvalidNodaDataException( $"Value \'{text}\'cannot be parsed as numeric format {reader.TokenType}." );
}
return Instant.FromUnixTimeTicks( ticks );
}
protected override void WriteJsonImpl( JsonWriter writer, Instant value, JsonSerializer serializer )
{
writer.WriteValue( value.ToUnixTimeTicks() );
}
}

Well, you can implement IPattern<T> yourself. Your parser would just need to use long.Parse then call Instant.FromUnixTicks. The formatter would just need to call Instant.ToUnixTimeTicks and format the result. Ideally, do both of those in the invariant culture.
You can then pass that pattern to the NodaPatternConverter<T> constructor - or just implement JsonConverter directly, to be honest.
Note that this will only give you tick resolution, which matches 1.x but may lose data with 2.x values.
I'd strongly encourage you to move away from this format as soon as you can though.

Related

Override JsonDeserializer Behavior

The generator has created a field of type OffsetDateTime:
#Nullable
#ElementName("DocDate")
private OffsetDateTime docDate;
But the server actually returns dates in the format: YYYY-mm-dd i.e. 2021-03-07
When using the generated code I get the following warnings:
WARN - Not deserializable: 2021-03-07
What is the correct way to override the deserialization of these fields? Or have these fields deserialize correctly?
An OffsetDateTime should have both a date and a time. The data your service responds with is lacking the time part. As per the OData V4 ABNF, this is not allowed (assuming your service is a V4 service):
dateTimeOffsetValue = year "-" month "-" day "T" hour ":" minute [ ":" second [ "." fractionalSeconds ] ] ( "Z" / SIGN hour ":" minute )
One way to solve this is to change the property type. You could either:
Change it to Edm.Date in the specification
Or change it to LocalDate in the generated code.
Of course this only makes sense if the service will always respond with a date.
Edit: If you really need to register a custom type adapter (e.g. because the service violates the JSON format) you could override the GsonVdmAdapterFactory:
public <T> TypeAdapter<T> create( #Nonnull final Gson gson, #Nonnull final TypeToken<T> type )
{
if( LocalDate.class.isAssignableFrom(rawType) ) {
return (TypeAdapter<T>) new CustomLocalDateTypeAdapter();
} else {
return super.create(gson, type);
}
}
However, this also requires changing the generated code, because there is currently no convenience to pass a custom type adapter as parameter. Change #JsonAdapter(com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class) to reference your custom factory.
Still, I'd recommend using one of the above workarounds until the service is fixed.

Dart: How do I convert an array of objects to an array of hashmaps?

I want to convert my objects to hashmaps so I can use the Flutter method channels to send data to Android.
I've thought of iterating through and mapping them one by one, but there's got to be a more elegant way to do this...
Example:
Object
class Something {
Something(this.what, this.the, this.fiddle);
final String what;
final int the;
final bool fiddle;
}
Somewhere else
List<Something> listOStuff = List<Something>.generate(10, (int index){
return Something(index.toString(), index, false,);
});
List<Map<String, dynamic>> parseToMaps(List<Something> listOStuff){
List<Map<String, dynamic>> results;
// do something crazy to get listOStuff into Map of primitive values for each object
// preferably a built in method of some sort... otherwise, i guess i'll just iterate...
// maybe even an imported package if such thing exists
return results;
}
List<Map<String, dynamic>> listOMaps = parseToMaps(listOStuff);
Something like this in Java
You can use the map and return the object that you want:
List<Map<String, dynamic>> listOMaps = listOStuff
.map((something) => {
"what": something.what,
"the": something.the,
"fiddle": something.fiddle,
})
.toList();
I'm not sure what exactly you're looking for, but there is a way to have custom objects encoded without having to specify it directly when you call the method.
What you have to do is implement a MethodCodec and/or MessageCodec that defines how your object is encoded and decoded. The easiest way is probably to subclass StandardMethodCodec and/or StandardMessageCodec (it might be enough to override StandardMessageCodec and pass it to StandardMessageCodec).
If you implement read & write correctly for your object, then all you have to do is pass the list of objects directly to your method call and flutter will handle the encoding.
Note that there are corresponding classes on the Android & iOS sides of things that you could use to have the data decoded directly to objects, and in fact you might have to implement them to get things to work depending on how you do it.

Invoking a functions based on string name in java

I need a logic to replace the following code.
void invokeMethod(String action){
if ("echo".equals(action)) {
//call echo
echo();
}
else if ("dump".equals(action)) {
// call dump
dump();
}
... goes on
}
switch case with string parameter won't work in java 1.6.
Can I do it a better way ?
I used a java hashmap with action as key and random integer as value. Whenever perticular action is asked to invoke, fetch the integer from the hashmap and use switch case now (In above problem, string comparison was very operation, replaced the same with integers).

Setting types of parsed values in Antlr

I have a rule that looks like this:
INTEGER : [0-9]+;
myFields : uno=INTEGER COMMA dos=INTEGER
Right now to access uno I need to code:
Integer i = Integer.parseInt(myFields.uno.getText())
It would be much cleaner if I could tell antler to do that conversion for me; then I would just need to code:
Integer i = myFields.uno
What are my options?
You could write the code as action, but it would still be explicit conversion (eventually). The parser (like every parser) parses the text and then it's up to "parsing events" (achieved by listener or visitor or actions in ANTLR4) to create meaningful structures/objects.
Of course you could extend some of the generated or built-in classes and then get the type directly, but as mentioned before, at some point you'll always need to convert text to some type needed.
A standard way of handling custom operations on tokens is to embed them in a custom token class:
public class MyToken extends CommonToken {
....
public Integer getInt() {
return Integer.parseInt(getText()); // TODO: error handling
}
}
Also create
public class MyTokenFactory extends TokenFactory { .... }
to source the custom tokens. Add the factory to the lexer using Lexer#setTokenFactory().
Within the custom TokenFactory, override the method
Symbol create(int type, String text); // (typically override both factory methods)
to construct and return a new MyToken.
Given that the signature includes the target token type type, custom type-specific token subclasses could be returned, each with their own custom methods.
Couple of issues with this, though. First, in practice, it is not typically needed: the assignment var is statically typed, so as in the the OP example,
options { TokenLabelType = "MyToken"; }
Integer i = myFields.uno.getInt(); // no cast required
If Integer is desired & expected, use getInt(). If Boolean ....
Second, ANTLR options allows setting a TokenLabelType to preclude the requirement to manually cast custom tokens. Use of only one token label type is supported. So, to use multiple token types, manual casting is required.

Grails: How to make everything I create Upper Case?

I am currently using CSS to change everything I write to upperCase when I create an entry, but that is not enough. When I save things, the text shown in the text fields is upper case, but the real value that Grails stores stays in lower case.
I am assuming I'd need to change something in the controller or anything.
Maybe transforming the $fieldValue CSS could work??
Any ideas would help!
Thnks!
You could just write setters for your domain object?
class Domain {
String aField
void setAField( String s ){
aField = s?.toUpperCase()
}
}
I think you are asking how to change values on your domain objects to uppercase. If this is not the case please clarify the question.
You have a bunch of options. I would recommend
1) In a service method, before you save, using String.toUpperCase() to modify the appropriate values on the domain object.
or
2) You can use the underlying Hibernate interceptors by defining a beforeInsert method on your domain object, and doing the toUpperCase there. (see 5.5.1 of the grails documentation)
or
3) You could do this client side. However, if it is a "business requirement" that the values are stored as upper, then I recommend doing the translation server side. It is easier to wrap tests around that code....
Using annotations is cleanest approach
import org.grails.databinding.BindingFormat
class Person {
#BindingFormat('UPPERCASE')
String someUpperCaseString
#BindingFormat('LOWERCASE')
String someLowerCaseString
}
Here is link for it: Grails doc for data binding
You can use Groovy metaprogramming to change the setter for all domain class String-typed properties without actually writing a custom setter for each property.
To do this, add something like the following to the init closure of Bootstrap.groovy
def init = { servletContext ->
for (dc in grailsApplication.domainClasses) {
dc.class.metaClass.setProperty = { String name, value ->
def metaProperty = delegate.class.metaClass.getMetaProperty(name)
if (metaProperty) {
// change the property value to uppercase if it's a String property
if (value && metaProperty.type == String) {
value = value.toUpperCase()
}
metaProperty.setProperty(delegate, value)
} else {
throw new MissingPropertyException(name, delegate.class)
}
}
}
}

Resources