জ্যাকসন ডাটাবাইন্ড এনাম কেস সংবেদনশীল


106

সংক্ষিপ্ত সংবেদনশীল এমন এনাম মানগুলিতে আমি কীভাবে JSON স্ট্রিংকে ডিসরিয়ালাইজ করতে পারি? (জ্যাকসন ডেটাবাইন্ড ব্যবহার করে)

JSON স্ট্রিং:

[{"url": "foo", "type": "json"}]

এবং আমার জাভা পোজো:

public static class Endpoint {

    public enum DataType {
        JSON, HTML
    }

    public String url;
    public DataType type;

    public Endpoint() {

    }

}

এই ক্ষেত্রে, JSON এর সাথে deserialization "type":"json"যেখানে ব্যর্থ হবে সেখানে ব্যর্থ "type":"JSON"হবে। তবে আমি "json"নামকরণের কারণেও কাজ করতে চাই ।

পজো সিরিয়ালিকৃত করার ফলে উপরের ক্ষেত্রেও ফলাফল হয় "type":"JSON"

আমি @JsonCreatorএবং @ জেসনগেটর ব্যবহার করার কথা ভেবেছিলাম :

    @JsonCreator
    private Endpoint(@JsonProperty("name") String url, @JsonProperty("type") String type) {
        this.url = url;
        this.type = DataType.valueOf(type.toUpperCase());
    }

    //....
    @JsonGetter
    private String getType() {
        return type.name().toLowerCase();
    }

এবং এটা কাজ করে. তবে আমি ভাবছিলাম যে এর চেয়ে ভাল দ্রাবক আছে কিনা কারণ এটি আমার কাছে হ্যাকের মতো দেখাচ্ছে।

আমি একটি কাস্টম ডিসরিওলাইজারও লিখতে পারি তবে আমি অনেকগুলি বিভিন্ন POJO পেয়েছি যা এনামগুলি ব্যবহার করে এবং এটি বজায় রাখা শক্ত হবে।

যথাযথ নামকরণ কনভেনশন সহ এনামগুলিকে সিরিয়ালাইজ এবং ডিসরিয়ালাইজ করার আরও ভাল উপায়ের পরামর্শ কেউ দিতে পারেন?

আমি চাই না জাভাতে আমার এনামগুলি ছোট হাতের হয়ে উঠুক!

এখানে আমি ব্যবহার করেছি এমন কিছু পরীক্ষার কোড:

    String data = "[{\"url\":\"foo\", \"type\":\"json\"}]";
    Endpoint[] arr = new ObjectMapper().readValue(data, Endpoint[].class);
        System.out.println("POJO[]->" + Arrays.toString(arr));
        System.out.println("JSON ->" + new ObjectMapper().writeValueAsString(arr));

আপনি জ্যাকসনের কোন সংস্করণটি চালু করছেন? এই JIRA দেখুন একবার jira.codehaus.org/browse/JACKSON-861
আলেক্সি

আমি জ্যাকসন
২.২.৩

ঠিক আছে আমি সবেমাত্র
2.4.0

উত্তর:


38

সংস্করণ ২.৪.০ এ আপনি সমস্ত এনাম প্রকারের ( গিথুব ইস্যুটির লিঙ্ক ) জন্য একটি কাস্টম সিরিয়ালাইজার নিবন্ধন করতে পারেন । এছাড়াও আপনি নিজেরাই স্ট্যান্ডার্ড এনুম ডিসরিওলাইজার প্রতিস্থাপন করতে পারেন যা এনাম টাইপ সম্পর্কে সচেতন হবে। এখানে একটি উদাহরণ:

public class JacksonEnum {

    public static enum DataType {
        JSON, HTML
    }

    public static void main(String[] args) throws IOException {
        List<DataType> types = Arrays.asList(JSON, HTML);
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<Enum> modifyEnumDeserializer(DeserializationConfig config,
                                                              final JavaType type,
                                                              BeanDescription beanDesc,
                                                              final JsonDeserializer<?> deserializer) {
                return new JsonDeserializer<Enum>() {
                    @Override
                    public Enum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
                        Class<? extends Enum> rawClass = (Class<Enum<?>>) type.getRawClass();
                        return Enum.valueOf(rawClass, jp.getValueAsString().toUpperCase());
                    }
                };
            }
        });
        module.addSerializer(Enum.class, new StdSerializer<Enum>(Enum.class) {
            @Override
            public void serialize(Enum value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
                jgen.writeString(value.name().toLowerCase());
            }
        });
        mapper.registerModule(module);
        String json = mapper.writeValueAsString(types);
        System.out.println(json);
        List<DataType> types2 = mapper.readValue(json, new TypeReference<List<DataType>>() {});
        System.out.println(types2);
    }
}

আউটপুট:

["json","html"]
[JSON, HTML]

4
ধন্যবাদ, এখন আমি আমার পোজোর সমস্ত বয়লারপ্লেট অপসারণ করতে পারি :)
tom91136

আমি আমার প্রকল্পগুলিতে ব্যক্তিগতভাবে এটির পক্ষে পরামর্শ দিচ্ছি। আপনি যদি আমার উদাহরণটি দেখুন তবে এর জন্য প্রচুর বয়লারপ্লেট কোডের প্রয়োজন। ডি / সিরিয়ালাইজেশনের জন্য পৃথক বৈশিষ্ট্যগুলি ব্যবহার করার একটি সুবিধা হ'ল এটি জাভা-গুরুত্বপূর্ণ মানগুলির নাম (এনাম নামগুলি) ক্লায়েন্ট-গুরুত্বপূর্ণ মানগুলিতে (চমত্কার মুদ্রণ) ডিক্লুপ্স করে। উদাহরণস্বরূপ, যদি এটি এইচটিএমএল ডেটা টাইপকে HTML_DATA_TYPE এ পরিবর্তন করতে ইচ্ছুক থাকে, তবে কোনও কী নির্দিষ্ট করে থাকলে আপনি বাহ্যিক এপিআইকে প্রভাবিত না করে এটি করতে পারেন।
স্যাম বেরি

4
এটি একটি ভাল শুরু তবে এটি যদি আপনার এনাম জেসনপ্রোপার্টি বা জসনক্রিটর ব্যবহার করে তবে এটি ব্যর্থ হবে। ড্রপউইজার্ডে ফজিএনমমডুল রয়েছে যা আরও দৃ implementation বাস্তবায়ন।
পিক্সেল এলিফ্যান্ট

143

জ্যাকসন ২.৯

এটি এখন খুব সাধারণ, jackson-databind২.৯.০ এবং তার বেশি উপরে ব্যবহার করে

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);

// objectMapper now deserializes enums in a case-insensitive manner

পরীক্ষার সাথে সম্পূর্ণ উদাহরণ

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Main {

  private enum TestEnum { ONE }
  private static class TestObject { public TestEnum testEnum; }

  public static void main (String[] args) {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);

    try {
      TestObject uppercase = 
        objectMapper.readValue("{ \"testEnum\": \"ONE\" }", TestObject.class);
      TestObject lowercase = 
        objectMapper.readValue("{ \"testEnum\": \"one\" }", TestObject.class);
      TestObject mixedcase = 
        objectMapper.readValue("{ \"testEnum\": \"oNe\" }", TestObject.class);

      if (uppercase.testEnum != TestEnum.ONE) throw new Exception("cannot deserialize uppercase value");
      if (lowercase.testEnum != TestEnum.ONE) throw new Exception("cannot deserialize lowercase value");
      if (mixedcase.testEnum != TestEnum.ONE) throw new Exception("cannot deserialize mixedcase value");

      System.out.println("Success: all deserializations worked");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

4
এই এক সোনার!
বিকাশ প্রসাদ

8
আমি 2.9.2 ব্যবহার করছি এবং এটি কাজ করে না। এর দ্বারা সৃষ্ট: com.fasterxml.jackson.databind.exc.InuthorFormatException: প্রকারের মানটি deserialize করা যায় না .... স্ট্রিং "পুরুষ" থেকে জেন্ডার: ঘোষিত এনাম উদাহরণের নামের একটি নয়: [ফ্যামেল, MALE]
জর্ডান সিলভা

@ জোর্দানসিলভা এটি অবশ্যই v2.9.2 এর সাথে কাজ করে। আমি যাচাইয়ের জন্য পরীক্ষার সাথে একটি পূর্ণ কোড উদাহরণ যুক্ত করেছি। আমি জানি না আপনার ক্ষেত্রে কী ঘটেছে, তবে উদাহরণ কোডটি jackson-databind২.৯.২ সহ চালানো প্রত্যাশার সাথে নির্দিষ্টভাবে কাজ করে।
davnicwil

8
স্প্রিং বুট ব্যবহার করে, আপনি কেবল সম্পত্তিটি যুক্ত করতে পারেনspring.jackson.mapper.accept-case-insensitive-enums=true
আর্ন বার্মিস্টার

4
@ জর্দানসিলভা সম্ভবত আপনি যেমনটি করেছেন তেমন প্যারামিটারগুলিতে এনামকে ডিসিজায়াল করার চেষ্টা করছেন? =) আমি আমার সমস্যার সমাধান করেছি এবং এখানে উত্তর দিয়েছি। আশা করি এটি সহায়তা করতে পারে
কনস্টান্টিন জিউবিন

86

আমি আমার প্রকল্পে এই একই সমস্যাটিতে ছড়িয়ে পড়েছি, আমরা সিদ্ধান্ত নিয়েছি যে আমাদের @JsonValueএনামগুলি যথাক্রমে সিরিয়ালায়ন এবং ডিসরিয়ালাইজেশনের জন্য একটি স্ট্রিং কী এবং ব্যবহার এবং একটি স্ট্যাটিক কনস্ট্রাক্টর দিয়ে তৈরি করব।

public enum DataType {
    JSON("json"), 
    HTML("html");

    private String key;

    DataType(String key) {
        this.key = key;
    }

    @JsonCreator
    public static DataType fromString(String key) {
        return key == null
                ? null
                : DataType.valueOf(key.toUpperCase());
    }

    @JsonValue
    public String getKey() {
        return key;
    }
}

4
এটি হওয়া উচিত DataType.valueOf(key.toUpperCase())- অন্যথায়, আপনি আসলে কিছুই পরিবর্তন করেন নি। কোনও return (null == key ? null : DataType.valueOf(key.toUpperCase()))
এনপিই

4
@ ধরায় ভাল। আমি সম্পাদনা করেছি। এছাড়াও, জ্যাক্স-আরএসের সাথে সুন্দরভাবে খেলতে পদ্ধতিটির নাম পরিবর্তন করে "স্ট্রিং" এ নামকরণ করা হয়েছে ।
স্যাম বেরি

4
আমি এই পদ্ধতির পছন্দ করেছিলাম, তবে কম ভার্বোস রূপের জন্য গিয়েছিলাম, নীচে দেখুন।
লিনাক

4
দৃশ্যত keyক্ষেত্র অপ্রয়োজনীয়। ইন getKey, আপনি কেবল করতে পারেনreturn name().toLowerCase()
yair

4
আমি যে ক্ষেত্রে এনএসের নাম রাখতে চাইছি সেই ক্ষেত্রে আমি মূল ক্ষেত্রটি পছন্দ করি j আমার ক্ষেত্রে কোনও উত্তরাধিকার ব্যবস্থা প্রেরণ করা মানটির জন্য নামটি মনে রাখতে সত্যই সংক্ষিপ্ত এবং শক্ত পাঠায় এবং আমি আমার জাভা এনামের জন্য আরও ভাল নামটিতে অনুবাদ করতে এই ক্ষেত্রটি ব্যবহার করতে পারি।
গ্রিচ করুন

50

জ্যাকসন ২.6 থেকে আপনি সহজেই এটি করতে পারেন:

    public enum DataType {
        @JsonProperty("json")
        JSON,
        @JsonProperty("html")
        HTML
    }

একটি সম্পূর্ণ উদাহরণের জন্য, এই টুকরোটি দেখুন


27
মনে রাখবেন যে এটি করলে সমস্যাটি বিপরীত হবে। এখন জ্যাকসন কেবল ছোট হাতের অক্ষর গ্রহণ করবে এবং কোনও বড় বা মিশ্র-ক্ষেত্রে মানগুলি প্রত্যাখ্যান করবে।
পিক্সেল এলিফ্যান্ট

30

আমি স্যাম বি এর সমাধানের জন্য গিয়েছিলাম তবে একটি সহজ বৈকল্পিক।

public enum Type {
    PIZZA, APPLE, PEAR, SOUP;

    @JsonCreator
    public static Type fromString(String key) {
        for(Type type : Type.values()) {
            if(type.name().equalsIgnoreCase(key)) {
                return type;
            }
        }
        return null;
    }
}

আমি এটিকে সহজ বলে মনে করি না। DataType.valueOf(key.toUpperCase())আপনার লুপ রয়েছে এমন একটি সরাসরি ইনস্ট্যান্টেশন। এটি একটি খুব অসংখ্য এনামের জন্য একটি সমস্যা হতে পারে। অবশ্যই, valueOfএকটি আইল্যাগালআর্গমেন্ট এক্সেপশন নিক্ষেপ করতে পারেন, যা আপনার কোড এড়ানো হয়, তাই যদি আপনি ব্যতিক্রম চেকিংয়ের ক্ষেত্রে নাল চেকিং পছন্দ করেন তবে এটি একটি ভাল উপকার।
প্যাট্রিক এম

24

আপনি যদি 2.1.xজ্যাকসনের সাথে স্প্রিং বুট ব্যবহার করেন তবে আপনি 2.9সহজেই এই অ্যাপ্লিকেশন সম্পত্তিটি ব্যবহার করতে পারেন:

spring.jackson.mapper.accept-case-insensitive-enums=true


4

যারা জিইটি প্যারামিটারগুলিতে এনামকে উপেক্ষা করার মামলাটি ডিসিরিয়ালাইজ করার চেষ্টা করেন তাদের পক্ষে এসিসিপিT_CASE_INSENSITIVE_ENUMS সক্ষম করা কোনও ভাল করবে না। এটি সহায়তা করবে না কারণ এই বিকল্পটি কেবল দেহের deserialization জন্য কাজ করে । পরিবর্তে এটি চেষ্টা করুন:

public class StringToEnumConverter implements Converter<String, Modes> {
    @Override
    public Modes convert(String from) {
        return Modes.valueOf(from.toUpperCase());
    }
}

এবং তারপর

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToEnumConverter());
    }
}

উত্তর এবং কোড নমুনার থেকে এসেছ এখানে


1

@ কনস্টান্টিন জিউবিনের কাছে ক্ষমা চেয়ে, তাঁর উত্তর আমার যা প্রয়োজন তার কাছাকাছি ছিল - তবে আমি তা বুঝতে পারি নি, সুতরাং আমার কীভাবে এটি করা উচিত বলে মনে হচ্ছে:

যদি আপনি কোনও এনাম টাইপের কেসকে সংবেদনশীল হিসাবে ডিসরিয়ালাইজ করতে চান - যেমন আপনি পুরো অ্যাপ্লিকেশনটির আচরণটি পরিবর্তন করতে বা করতে চান না বা করতে পারেন না তবে আপনি কেবলমাত্র এক প্রকারের জন্য একটি কাস্টম ডিসারিয়ালাইজার তৈরি করতে পারেন - উপ-শ্রেণিবদ্ধকরণ StdConverterএবং বল প্রয়োগ করে জ্যাকসন এটি JsonDeserializeটীকাটি ব্যবহার করে কেবলমাত্র প্রাসঙ্গিক ক্ষেত্রে ব্যবহার করবেন ।

উদাহরণ:

public class ColorHolder {

  public enum Color {
    RED, GREEN, BLUE
  }

  public static final class ColorParser extends StdConverter<String, Color> {
    @Override
    public Color convert(String value) {
      return Arrays.stream(Color.values())
        .filter(e -> e.getName().equalsIgnoreCase(value.trim()))
        .findFirst()
        .orElseThrow(() -> new IllegalArgumentException("Invalid value '" + value + "'"));
    }
  }

  @JsonDeserialize(converter = ColorParser.class)
  Color color;
}

1

জ্যাকসনের এনামগুলিকে সংবেদনশীল deserialization করতে অনুমতি দেওয়ার জন্য, কেবল application.propertiesআপনার বসন্ত বুট প্রকল্পের ফাইলটিতে নীচের সম্পত্তি যুক্ত করুন ।

spring.jackson.mapper.accept-case-insensitive-enums=true

আপনার যদি প্রপার্টি ফাইলের ইয়ামল সংস্করণ থাকে তবে আপনার application.ymlফাইলটিতে নীচে সম্পত্তি যুক্ত করুন।

spring:
  jackson:
    mapper:
      accept-case-insensitive-enums: true

ধন্যবাদ, স্প্রিং বুটে এই কাজগুলি নিশ্চিত করতে পারে।
জুনাস ভালি 7

0

Com.fasterxml.jackson.databind.util.EnumResolver এ সমস্যা যুক্ত হয়েছে । এটি এনাম মান ধরে রাখতে হ্যাশম্যাপ ব্যবহার করে এবং হ্যাশম্যাপ কেস সংবেদনশীল কীগুলি সমর্থন করে না।

উপরের উত্তরে, সমস্ত অক্ষর বড় হাতের বা ছোট হাতের হওয়া উচিত। তবে আমি এটির সাথে এনামগুলির জন্য সমস্ত (ইন) সংবেদনশীল সমস্যাগুলি স্থির করেছি:

https://gist.github.com/bhdrk/02307ba8066d26fa1537

কাস্টমডিজারিয়ালাইজার্স.জভা

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.std.EnumDeserializer;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.util.EnumResolver;

import java.util.HashMap;
import java.util.Map;


public class CustomDeserializers extends SimpleDeserializers {

    @Override
    @SuppressWarnings("unchecked")
    public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException {
        return createDeserializer((Class<Enum>) type);
    }

    private <T extends Enum<T>> JsonDeserializer<?> createDeserializer(Class<T> enumCls) {
        T[] enumValues = enumCls.getEnumConstants();
        HashMap<String, T> map = createEnumValuesMap(enumValues);
        return new EnumDeserializer(new EnumCaseInsensitiveResolver<T>(enumCls, enumValues, map));
    }

    private <T extends Enum<T>> HashMap<String, T> createEnumValuesMap(T[] enumValues) {
        HashMap<String, T> map = new HashMap<String, T>();
        // from last to first, so that in case of duplicate values, first wins
        for (int i = enumValues.length; --i >= 0; ) {
            T e = enumValues[i];
            map.put(e.toString(), e);
        }
        return map;
    }

    public static class EnumCaseInsensitiveResolver<T extends Enum<T>> extends EnumResolver<T> {
        protected EnumCaseInsensitiveResolver(Class<T> enumClass, T[] enums, HashMap<String, T> map) {
            super(enumClass, enums, map);
        }

        @Override
        public T findEnum(String key) {
            for (Map.Entry<String, T> entry : _enumsById.entrySet()) {
                if (entry.getKey().equalsIgnoreCase(key)) { // magic line <--
                    return entry.getValue();
                }
            }
            return null;
        }
    }
}

ব্যবহার:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;


public class JSON {

    public static void main(String[] args) {
        SimpleModule enumModule = new SimpleModule();
        enumModule.setDeserializers(new CustomDeserializers());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(enumModule);
    }

}

0

আমি আইগো ফার্নান্দেজ এবং পল সমাধানের একটি পরিবর্তন ব্যবহার করেছি।

আমার অনুরোধে আমার একটি এনাম ছিল যা ক্ষেত্রে সংবেদনশীল হওয়া দরকার

@POST
public Response doSomePostAction(RequestObject object){
 //resource implementation
}



class RequestObject{
 //other params 
 MyEnumType myType;

 @JsonSetter
 public void setMyType(String type){
   myType = MyEnumType.valueOf(type.toUpperCase());
 }
 @JsonGetter
 public String getType(){
   return myType.toString();//this can change 
 }
}

-1

কেস-সংবেদনশীল পদ্ধতিতে (প্রশ্নটিতে পোস্ট করা কোডের উপর ভিত্তি করে) ডিজিটাইজেশন করতে চাইলে আমি কখনও কখনও এনামগুলিকে কীভাবে পরিচালনা করি:

@JsonIgnore
public void setDataType(DataType dataType)
{
  type = dataType;
}

@JsonProperty
public void setDataType(String dataType)
{
  // Clean up/validate String however you want. I like
  // org.apache.commons.lang3.StringUtils.trimToEmpty
  String d = StringUtils.trimToEmpty(dataType).toUpperCase();
  setDataType(DataType.valueOf(d));
}

যদি এনামটি তুচ্ছ হয় না এবং এইভাবে তার নিজস্ব শ্রেণিতে আমি সাধারণত ছোট হাতের স্ট্রিংগুলি পরিচালনা করতে একটি স্ট্যাটিক পার্স পদ্ধতি যুক্ত করি।


-1

জ্যাকসনের সাথে এনাম ডিজাইরিজ করা সহজ। আপনি যখন স্ট্রিংয়ের উপর ভিত্তি করে এনামকে ডিজিজালাইজ করতে চান তখন আপনার এনামের জন্য কনস্ট্রাক্টর, গেটর এবং সেটারের দরকার পড়ে that এছাড়াও এনাম ব্যবহার করে এমন একটি সেটার থাকতে হবে যা স্ট্রিং নয়, প্যারাম হিসাবে ডেটাটাইপ প্রাপ্ত করে must

public class Endpoint {

     public enum DataType {
        JSON("json"), HTML("html");

        private String type;

        @JsonValue
        public String getDataType(){
           return type;
        }

        @JsonSetter
        public void setDataType(String t){
           type = t.toLowerCase();
        }
     }

     public String url;
     public DataType type;

     public Endpoint() {

     }

     public void setType(DataType dataType){
        type = dataType;
     }

}

যখন আপনার জসন থাকে, আপনি জ্যাকসনের অবজেক্টম্যাপার ব্যবহার করে এন্ডপয়েন্ট পয়েন্টটি ডিসরিয়ালাইজ করতে পারেন:

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
try {
    Endpoint endpoint = mapper.readValue("{\"url\":\"foo\",\"type\":\"json\"}", Endpoint.class);
} catch (IOException e1) {
        // TODO Auto-generated catch block
    e1.printStackTrace();
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.