jackson-mapper version 1.9.12 EnumDeserializer issue - java

i am trying to map String to enum Object using Jackson ObjectMapper.readValue(String,Class) API, problem is Lets SAY my json string contains a Task Object with Action enum as below
public enum Action {
#XmlEnumValue("Add")
ADD("Add"),
#XmlEnumValue("Amend")
AMEND("Amend"),
#XmlEnumValue("Delete")
DELETE("Delete"),
#XmlEnumValue("Pending")
PENDING("Pending");
private final String value;
Action(String v) {
value = v;
}
public String value() {
return value;
}
public static Action fromValue(String v) {
for (Action c: Action.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
and the jason string will be like this "{"action":"Add"}" then ObjectMapper.readValue(jsonString, Task.Class) throws
org.codehaus.jackson.map.deser.StdDeserializationContext.weirdStringException(StdDeserializationContext.java:243) for Action Add because it cant convert this Enum.
I tried adding custom Desiserializer, But EnumDeserializer getting called anyway. any ideas?
All objects are JAXB generated, so annotations not possible.
Thanks for the help

Have you tried:
new ObjectMapper().setAnnotationIntrospector(new JaxbAnnotationIntrospector()).readValue()

Related

Create a class with JSON conditionally format

how can i convert this field in to Java Class field where myText field can be conditionally one of this format:
{
"myText" : "Values
}
{
"myText" : ["text": "Values", "text2" : "Values"]
}
public class MyClass {
private String myText;
//or
private MyText myText;
}
public class MyText {
private String text1;
private String text2;
}
Welll.. you shouldn't do that in the first place.
But sometimes there is a case when some badly written rest api will return something like that. I have one solution which we are using only for parsing and sending through data.
So it works for us, but may not work for you
public class ListOrNotList {
protected ListOrNotList() {
}
protected String get(Object item) {
if (item instanceof String) {
return (String)item;
} else if (item instanceof List) {
for (Object it : (List)item) {
if (it instanceof LinkedHashMap) {
LinkedHashMap hashMap = (LinkedHashMap) it;
if (hashMap.containsKey("value")) {
Object value = hashMap.get("value");
if (value == null) {
return null;
}
return value.toString();
}
}
}
}
return null;
}
}
Then you make your class MyClass which extends from ListOrNotList
public class MyClass extends ListOrNotList {
private Object myText;
public String getMyText() {
return get(myText); //This is method from ListOrNotList class
}
}
This is not 100% working solution for OP, just an idea how it can be handled
In this example get function returns string or first element from List
you could also implement casting to T type in that method if you have a specific type and want to use any operations on it.
If you are creating api yourself then don't do things like returning different datatypes with the same name, it's bad practice and will harm development when anyone will try to integrate it in an app (including you after few months)

Several decimal for double in JAXB

I create XML with JAXB, and I want to put double inside tags:
#XmlElement(name = "TaxFree")
private double taxFreeValue;
When I set value with setTaxFreeValue(4.5); in tags shows <TaxFree>4.5<TaxFree>
Is it possible in JAXB to get this <TaxFree>4.500<TaxFree> without transfer double to string?
The simplest way is this
double taxFreeValue;
#XmlElement(name = "TaxFree")
private String getTaxFree() {
return String.format("%.3f", taxFreeValue);
}
Note that you can give this method any name and make it private JAXB dont care as soon as the annotation is present.
You can use an XmlAdapter to convert from the double value to the desired text (String) representation.
Using JAXB generated class for an element that requires an integer with a pattern
The cleanest way I've found is to use XMLAdapter. Create a DoubleAdapter class with:
public class DoubleAdapter extends XmlAdapter<String, Double> {
#Override
public Double unmarshal(String v) throws Exception {
if (v == null || v.isEmpty() || v.equals("null")) {
return null;
}
return Double.parseDouble(v);
}
#Override
public String marshal(Double v) throws Exception {
if (v == null) {
return null;
}
//Edit the format to your needs
return String.format("%.3f", v);
}
}
To use it, simply add the annotation.
#XmlElement(name = "TaxFree")
#XmlJavaTypeAdapter(DoubleAdapter.class)
private double taxFreeValue;

json request enum

We have RestWS where need to pass request in JSON format. This request contains different type of values such as String, List, enum etc.
We figured out how need to pass String and List (see below) but not sure how to pass enum in JSON request object.
Sample JSON Request for List and String in request:
{"firstparam":["195","196"],"secondparam":"test"}
First param is List and second param is String. Similarly we need to know how we can pass enum (also in the above request).
Sample enum class:
#XmlType(name = "Type")
#XmlEnum
public enum Type {
#XmlEnumValue("New")
NEW("New"),
#XmlEnumValue("Delete")
DELETE("Delete"),
#XmlEnumValue("Process")
PROCESS("Process");
private final String value;
WorkingStatusType(String v) {
value = v;
}
public String value() {
return value;
}
public static WorkingStatusType fromValue(String v) {
for (WorkingStatusType c: WorkingStatusType.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
This Google JSON style guide might help you.

SimpleXML enum case-sensitivity

I have been trying to create an XML using the simplexml library (v2.6.2)
http://simple.sourceforge.net/home.php
The XML I want to create has to hold an enum value, which should be case-sensitive. Following is the POJO :
package pojos;
public enum MyEnum {
NEW("new"),
OLD("old");
private final String value;
MyEnum(String v)
{
value = v;
}
public String value() {
return value;
}
public static MyEnum fromValue(String v) {
for (MyEnum c: MyEnum.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
Following is the serializer code :
import java.io.File;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import pojos.MyEnum;
public class TestEnum {
/**
* #param args
* #throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Serializer serializer = new Persister();
MyEnum example = MyEnum.NEW;
File result = new File("myenum.xml");
serializer.write(example, result);
}
}
The resultant output :
<myEnum>NEW</myEnum>
The desired output :
<myEnum>new</myEnum>
How should I proceed ? I cannot change the variable name in the enum as it happens to be the keyword "new" !
Thanks.
After some investigation of the source code, i have discovered that the library uses interface Transform to transform values to Strings. The default behavior of enum transformations is defined by class EnumTransform. In order to customize that, you can define you own Transform class. The following version of Transform implementation would call toString() instead of the default name() on the enum objects.
class MyEnumTransform implements Transform<Enum> {
private final Class type;
public MyEnumTransform(Class type) {
this.type = type;
}
public Enum read(String value) throws Exception {
for (Object o : type.getEnumConstants()) {
if (o.toString().equals(value)) {
return (Enum) o;
}
}
return null;
}
public String write(Enum value) throws Exception {
return value.toString();
}
}
Transform objects are returned from match method by objects of Matcher interface. There could be several Matcher objects. The library tries them one by one until it finds one that returns a non-null Transformer object. You can define your own Matcher object and pass it as argument to the constructor of Persister class. This object will get the highest priority.
Persister serializer = new Persister(new Matcher() {
public Transform match(Class type) throws Exception {
if (type.isEnum()) {
return new MyEnumTransform(type);
}
return null;
}
});
Finally, you wont forget to define a toString method on your enum classes. Then the combination of codes above will do you the work of encoding enum objects using their toString values.
You should override toString()
#Override
public String toString() {
return this.value.toLowerCase();
}
Then write results using
serializer.write(example.toString(), result);
I would look at the serializer code and undestand what that is doing, as you have not annotated any of your fields...which (according to their docs) should throw an an exception.

Trim all strings elements in a complex object

Can I write a generic method to trim all strings within an complex object (object containing other objects)? Should java reflection api be used to achieve this?Thanks.
I have provided a sample below. However in reality there could be multiple objects within objects. Each object might contain a collection of String or collection of other objects which may contain String. Is there a way to trim the Strings - ones directly with the objects and ones within collection.
public class School{
private List<Course> courses;
private List<Student> students;
// Getters and Setters
}
public class Course{
private String name;
private String xxx;
private String yyy;
private List<String> zzzList;
}
public class Student{
private Map<String,String> xxx;
private List<Course> courseList;
}
Yes, reflection is the way. Basically, you need to:
get the class of the top level object (with [object].getClass())
get all the fields of the object (with clazz.getFields() - beware, it works only with public fields)
check if the field is String (either get field.getType() and check it's a string, or do a field.get(the object) and a instanceof String)
if it's the case, replace the string in the object with the trimmed one, using field.set([your object],[trimmed string])
if the field is an object but not a string, call your method recursively
That will do the trick.
---- just seen your update
Trimming strings in collection will be more tricky, since the strings are not exposed as public fields of the collection (List for example).
You will need something more clever, that will check if an object is an instance of List, or Map, or etc... (or a derived class!).
Main problem is also that java generics are done with erasing type at compile type. So you cannot know that your field is List[String] or List[Integer] or whatever. Every List[?] becomes List.
Still you can try to do it like that:
if field type is List
iterate through the list values
if a value is instanceof String, you have to remove it from the list and insert in place the trimmed version
if a value is an object, there you go again recursively with your method.
Not very interesting in real life samples, but more on a library side maybe.
Long way to go though!
Yes, you can do that with reflection, quite easily. Just check if the field is instanceof String.
The exact way to do it depends on your object structure.
/*********************************************************************************************
* Trim first level children of string type in this object
* #param obj which all string properties to be trimmed
*********************************************************************************************/
public static void trimAll(final Object obj)
throws LocalException
{
if (obj==null) return;
final Class c = obj.getClass();
final Method[] methods = c.getMethods();
final Class[] SETTER_ARGS = new Class[]{String.class};
final Object[] SETTER_VAL = new Object[1];
final String SET = "set";
final String GET = "get";
final String SPACE = "\u0020";
final String TAB = "\t";
for (final Method m:methods)
{
try
{
final String name=m.getName();
if (
name.length()>GET.length()
&& name.indexOf(GET)==0
&& m.getReturnType().equals(String.class)
&& m.getParameterTypes().length==0)
{
final String v = (String)m.invoke(obj);
if (v!=null && (v.contains(SPACE) || v.contains(TAB)) )
{
final Method setter=c.getMethod(SET+name.substring(3),SETTER_ARGS);
if (setter!=null)
{
SETTER_VAL[0]=v.trim();
setter.invoke(obj,SETTER_VAL);
}
}
}
}
catch (final Throwable e)
{
throw new LocalException(LocalException.EC_GENERAL_EXCEPTION,e);
}
}
}
We can also use Jackson to serialize and then deserialize the object. While deserializing we can use custom deserializer to trim all the String values.
Create a deserializer like this:
public class TrimStringToNullDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException {
String value = jsonParser.getValueAsString();
if (isNull(value)) {
return null;
}
value = value.trim();
if (value.length() == 0) {
value = null;
}
return value;
}
And then we can use Jackson to trim all values:
public class TrimStringToNullConfiguration {
private ObjectMapper objectMapper;
public Client trimToNull(Client inputClient) throws JsonProcessingException {
return getObjectMapper().readValue(getObjectMapper().writeValueAsString(inputClient), Client.class);
}
private ObjectMapper getObjectMapper() {
if (isNull(objectMapper)) {
objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new TrimStringToNullDeserializer());
objectMapper.registerModule(module);
}
return objectMapper;
}
I have placed a working example over here.
private <T> T toTrim(T t) {
Field[] fields = t.getClass().getFields();
for (Field field : fields) {
try {
if (field.get(t) instanceof String) {
Object o = field.get(t);
String s = (String) o;
field.set(t, s.trim().toUpperCase());
}
} catch (IllegalAccessException e) {
log.info("Error converting field "+ field.getName() );
}
}
return t;
}
if (yourObject instanceof String){
yourObject = yourObject.trim();
}
Hope it helps :)

Categories