一般用Jackson怎么把逗号分隔的字符串格式化成列表?

hkmswyz6  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(169)

我有一个json像:

{
  "names": "John, Tom",
  "values": "8, 9",
  "statuses": "yes, no"
}

字符串
并希望实现以下目标:

class Bean {
  private List<String> names;
  private List<Integer> values;
  private List<StatusEnum> statuses;
}


我知道单独实现StringToStringListDeserializerStringToIntegerListDeserializerStringToStatusEnumListDeserializer是可行的。但是还有很多其他的内容类型,包括自定义类型。我试过:

public class StringToListDeserializer<T> extends JsonDeserializer<List<T>> implements ContextualDeserializer
    public List<T> deserialize(JsonParser p, DeserializationContext context) throws IOException {
        JavaType javaType = property.getType();
        if (p.hasToken(JsonToken.VALUE_STRING)) {
            String text = p.getText();
            if (StringUtils.isBlank(text)) {
                return null;
            }
            List<T> list = new LinkedList<>();
            JavaType contentType = javaType.getContentType();
            JsonDeserializer<Object> deserializer = context.findNonContextualValueDeserializer(contentType);
            for (String s : text.split(DELIMITER)) {

                // todo how to deserialize the string to a known type?

            }
            return list;
        }
        return context.readValue(p, javaType);
    }


我不知道如何将字符串转换为已知的内容类型。有没有办法实现一个通用的转换器?

q7solyqu

q7solyqu1#

为了避免手动的格式化和处理所有可能的类型,我们可以使用一个事实,即列表中的所有元素都是JSON元素,当我们用引号(")char Package 它们时。
因此,我们可以将John, Tom转换为"John", "Tom",将8, 9转换为"8", "9"等等。
我们可以使用默认的Jackson行为,它允许处理意外的令牌。在我们的例子中,每当:STRING令牌出现时,JSON ARRAY是预期的。为了处理这些情况,我们可以使用com.fasterxml.jackson.databind.deser.DeserializationProblemHandler类。它可能看起来像下面这样:

class ComaSeparatedValuesDeserializationProblemHandler extends DeserializationProblemHandler {

    @Override
    public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, JsonToken token, JsonParser parser, String failureMsg) throws IOException {
        if (token == JsonToken.VALUE_STRING && targetType.isCollectionLikeType()) {
            return deserializeAsList(targetType, parser);
        }
        return super.handleUnexpectedToken(ctxt, targetType, token, parser, failureMsg);
    }

    private Object deserializeAsList(JavaType listType, JsonParser parser) throws IOException {
        String[] values = readValues(parser);

        ObjectMapper mapper = (ObjectMapper) parser.getCodec();
        JavaType itemType = listType.getContentType();

        List<Object> result = new ArrayList<>();
        for (String value : values) {
            result.add(convertToItemType(mapper, itemType, value));
        }

        return result;
    }

    private Object convertToItemType(ObjectMapper mapper, JavaType contentType, String value) throws IOException {
        final String json = "\"" + value.trim() + "\"";

        return mapper.readValue(json, contentType);
    }

    private String[] readValues(JsonParser p) throws IOException {
        final String text = p.getText();

        return text.split(",");
    }
}

字符串
示例用法:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.google.common.base.Joiner;
import lombok.Data;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class ConvertStringToCollectionApp {
    public static void main(String[] args) throws IOException {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        ObjectMapper mapper = JsonMapper.builder()
                .addHandler(new ComaSeparatedValuesDeserializationProblemHandler())
                .build();

        Bean bean = mapper.readValue(jsonFile, Bean.class);

        print(bean.getNames());
        print(bean.getValues());
        print(bean.getStatuses());
    }

    private static void print(List<?> values) {
        values.stream().findFirst().ifPresent(value -> System.out.print(value.getClass().getSimpleName() + "s: "));
        System.out.println(Joiner.on(", ").join(values));
    }
}

@Data
class Bean {
    private List<String> names;
    private List<Integer> values;
    private List<StatusEnum> statuses;
}

enum StatusEnum {
    yes, no
}


以上应用程序适用于您的JSON有效载荷打印:

Strings: John, Tom
Integers: 8, 9
StatusEnums: yes, no


我使用LombokGuava库只是为了使它简单和简短,但它们并不是强制性的。

vshtjzan

vshtjzan2#

我也遇到了这个问题。我完成了你的代码中的TODO。

public class StringToListDeserializer extends JsonDeserializer<List<?>> implements ContextualDeserializer
    public List<?> deserialize(JsonParser p, DeserializationContext context) throws IOException {
        JavaType javaType = property.getType();
        if (p.hasToken(JsonToken.VALUE_STRING)) {
            String text = p.getText();
            if (StringUtils.isBlank(text)) {
                return null;
            }
            List<?> list = new LinkedList<>();
            JavaType contentType = javaType.getContentType();
            JsonDeserializer<Object> deserializer = context.findNonContextualValueDeserializer(contentType);

            // To write json String
            ObjectMapper objectMapper = new ObjectMapper();

            for (String s : text.split(DELIMITER)) {
                // As Michal Ziober said, it needs to be wrapped in quotation marks here
                String wrappedValue = objectMapper.writeValueAsString(s);

                // Generate JsonParser based on wrappedValue
                JsonParser wrappedJsonParser = p.getCodec().getFactory().createParser(wrappedValue);

                // Let JsonParser generate currentToken internally
                wrappedJsonParser.nextToken();

                // Deserialize the JSON string to a known type
                Object value = deserializer.deserialize(wrappedJsonParser, context);

                // Save the result
                list.add(value);

                // Should we do `wrappedJsonParser.close()`? I didn't try.

            }
            return list;
        }
        return context.readValue(p, javaType);
    }

字符串

xmd2e60i

xmd2e60i3#

您的Bean没有正确表示JSON。正确的版本应该如下所示

class Bean {
  private String names;
  private Integer values;
  private String statuses;
}

字符串
可以使用Object Mapper

ObjectMapper objectMapper = new ObjectMapper();
Bean bean = objectMapper.readValue(json, Bean.class);


最后,您可以将Bean对象分解为名称、值和状态的列表,以便进一步使用。

相关问题