浅析RestTemplate的Jackson消息转换器

ElasticSearch高级客户端启动时解决 Error creating bean with name ‘mappingJackson2HttpMessageConverter‘ 学习ElasticSearch时,写好往ehcahce index索引的代码,启动项目时,发生了Error creating bean with name 'mappingJackson2HttpMessageConverter’报错。 于是我在lib里寻找Jar包,发现引入了Jackson的包。但是pom文件里并没有引入相关依赖。我就查相关依赖,发现是从high-level-client里引入的 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SmA3Eeuc-1635417 阅读详情

一、前因后果

之前有写过一个关于上游接口不按规范导致的 Jackson 序列化问题(见https://blog.youkuaiyun.com/Remember_Z/article/details/119737998 或 https://www.yangguirong.com/archives/ji-yi-ci-fan-xu-lie-hua-yi-chang),大概就是接口文档中标明了某个字段是数组格式,但是当数组中只有一个对象时,这个属性会直接以对象的形式的返回,如下两种返回形式

// 当数组长度大于1时,返回值与文档是一致的
{
    "users": [
        {
            "name": "Jack",
            "age": 18
        },
        {
            "name": "John",
            "age": 20
        }
    ]
}

// 而当数组长度等于1时,返回格式就是下面的形式了
{
    "users": {
        "name": "Jack",
        "age": 18
    }
}

这还不是全部的问题,还有一种情况,假设 user 有一个属性 contact,是一个对象,存放的是用户的联系信息,正常情况下返回的 user 信息如下

{
    "name": "Jack",
    "age": 18,
    "contact": {
        "phone": "158****8888",
        "email": "jack@test.com"
    }
}

而如果某个用户联系信息为空,则接口有可能会返回 null,这是最理想的情况,也是一般做法,但是哈,他们的接口不一定会真的返回一个 null 给你,它有可能返回的是一个空串,如下

{
    "name": "Jack",
    "age": 18,
    "contact": ""
}

这种情况下,我正常的用 DTO 对象接收就有问题了。我之前的处理是通过自定义反序列器的形式进行手动转换,在字段比较少的时候还好,可以逐个加一下(见之前博客的说明)。但是如果字段较多,总不能一个个写吧,这种情况下,可以定义一个通用的泛型数组转换器,至于泛型擦除,可以通过额外定义一个辅助注解来进行解决

反序列化器

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.h3c.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ReflectionUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 对象转list反序列化器
 * <p>
 * 避免泛型擦除,借助{@link ObjListDeserializerType}注解来取类型
 *
 * @author yang guirong
 */
@Slf4j
public class ObjListDeserializer extends JsonDeserializer<List<?>> {

    private final String emptyJsonObjStr = "{}";
    private final String[] baseDataType = {"Byte", "Short", "Integer", "Long", "Float", "Double", "Boolean", "Character", "String"};

    public ObjListDeserializer() {
    }

    @Override
    public List<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
            throws IOException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        List<Object> list = new ArrayList<>();

        Class<?> aClass = jsonParser.getParsingContext().getCurrentValue().getClass();
        String fieldName = jsonParser.getParsingContext().getCurrentName();
        Field field = ReflectionUtils.findField(aClass, fieldName);
        if (field == null) {
            Field[] fields = aClass.getDeclaredFields();
            field = Arrays.stream(fields).filter(f -> {
                JsonProperty property = f.getDeclaredAnnotation(JsonProperty.class);
                return property != null && fieldName.equals(property.value());
            }).findFirst().orElse(null);
            if (field == null) {
                log.error("[deserialize]错误,类<{}>不存在名称为<{}>属性!", aClass.getName(), fieldName);
                return list;
            }
        }
        ObjListDeserializerType objListDeserializerType = field.getDeclaredAnnotation(ObjListDeserializerType.class);
        if (objListDeserializerType == null) {
            log.error("[deserialize]错误,类<{}>中名称为<{}>属性不存在ObjListDeserializerType注解!", aClass.getName(), fieldName);
            return list;
        }

        Class<?> type = objListDeserializerType.value();

        try {
            if (node instanceof TextNode && StrUtil.isBlank(node.textValue())) {
                return list;
            } else if (node.isArray()) {
                return JsonUtil.toList(node.toString(), type);
            } else if (node.isObject()) {
                String str = node.toString();
                if (isBaseDataType(type) && emptyJsonObjStr.equals(str)) {
                    return list;
                }
                Object dto = JsonUtil.toObj(str, type);
                list.add(dto);
                return list;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    @Override
    public List<?> getNullValue(DeserializationContext ctx) {
        return new ArrayList<>();
    }

    private boolean isBaseDataType(Class<?> type) {
        for (String str : baseDataType) {
            if (str.equals(type.getSimpleName())) {
                return true;
            }
        }
        return false;
    }
}

泛型辅助注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface ObjListDeserializerType {

    Class<?> value();

}

然后上面的例子中就可以通过如下的方式进行处理

@Data
public class UsersDTO {

    @ObjListDeserializerType(UserItem.class)
    @JsonDeserialize(using = ObjListDeserializer.class)
    private List<UserItem> users;

    @Data
    public static class UserItem {
        private String name;
        private Integer age;
        private ContactDTO contact;

        @Data
        public static class ContactDTO {
            private String phone;
            private String email;
        }
    }
}

这样就解决了第一种情况,但是第二种情况,也就是空串的问题还没有解决。网上有人说是用 @JsonInclude,但实际上是不行的,这个注解是用于序列化的,不是用于反序列化的。当然这种情况也可以使用自定义反序列器的方式进行解决,但是字段多了之后,还得一个个加,稍不留意就漏了某个字段。所以还得从全局的反序列策略来进行修改。

二、解决方案

首先,我在对接上游接口的时候,选择是通过 RestTemplate 直接调用的接口,所以肯定是要从这个类进行入手的。

我的 RestTemplate 配置类如下

@Configuration
public class RestTemplateConfig {

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setConnectTimeout(5000);
        factory.setReadTimeout(10000);
        return factory;
    }

    @Bean
    @ConditionalOnClass(RestTemplate.class)
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        RestTemplate restTemplate = restTemplateBuilder.build();
        restTemplate.setRequestFactory(clientHttpRequestFactory());
        return restTemplate;
    }

}

使用的是 RestTemplateBuilder 来进行构建(关于这个 Bean 的配置可以看配置类 RestTemplateAutoConfiguration),其中 build 方法如下

public RestTemplate build() {
    return configure(new RestTemplate());
}

可以看到实例化 RestTemplate 用的是无参构造方法,其实这个构造方法可以不用看,因为其中的 messageConverters 即便在构造方法中设置了,RestTemplateBuilder 的 configure 也会将之再次覆盖。

public <T extends RestTemplate> T configure(T restTemplate) {
    ...
    if (!CollectionUtils.isEmpty(this.messageConverters)) {
        restTemplate.setMessageConverters(new ArrayList<>(this.messageConverters));
    }
    ...
    return restTemplate;
}

而在项目运行中最终处理消息的是 MappingJackson2HttpMessageConverter ,因为 spring-boot-starter-web 中包含了 spring-boot-starter-json,而后者就包含 jackson-databind 等依赖,所以 MappingJackson2HttpMessageConverter 是一定存在的,但也存在其他的消息转换器,至于为何最终反序列化的时候会使用它,打断点看一下 RestTemplate 的 excute 方法就可以得出结果,最终将消息由 json 转换为实体类的是 HttpMessageConverterExtractor 的 extractData 方法,代码就不贴出来了,感兴趣的可以自行打断点看一下。

而 MappingJackson2HttpMessageConverter 转换消息,使用的是 ObjectMapper,ObjectMapper 是可以自定义某些特性的,其中的反序列特性有这么两个 ACCEPT_EMPTY_STRING_AS_NULL_OBJECT 和 ACCEPT_SINGLE_VALUE_AS_ARRAY 刚好可以满足之前提到的两个问题。前者将空串视作空对象(null),刚好可以解决第二个问题,而后者,对于类型声明是数组,但是只有一个值的时候可以自动转换为数组,刚好可以解决第一个问题,关键就在于如何让它生效。

前面提到了,因为使用的是 RestTemplateBuilder 来进行配置,所以不管 RestTemplate 的构造方法中如何进行定义 messageConverters,最后都会被 RestTemplateBuilder 的 configure 方法给覆盖,所以要弄清楚 RestTemplateBuilder 中 messageConverters 中的 MappingJackson2HttpMessageConverter 是怎么定义的。

首先,RestTemplateBuilder 的 Bean 定义如下

@Bean
@Lazy
@ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
    RestTemplateBuilder builder = new RestTemplateBuilder();
    return restTemplateBuilderConfigurer.configure(builder);
}

它的构造方法如下,messageConverters 是空的,不是我想要的结果

public RestTemplateBuilder(RestTemplateCustomizer... customizers) {
    Assert.notNull(customizers, "Customizers must not be null");
    this.requestFactoryCustomizer = new RequestFactoryCustomizer();
    this.detectRequestFactory = true;
    this.rootUri = null;
    this.messageConverters = null;
    this.interceptors = Collections.emptySet();
    this.requestFactory = null;
    this.uriTemplateHandler = null;
    this.errorHandler = null;
    this.basicAuthentication = null;
    this.defaultHeaders = Collections.emptyMap();
    this.customizers = copiedSetOf(customizers);
    this.requestCustomizers = Collections.emptySet();
}

而 configure 如下,可以看到, RestTemplateBuilder 的 messageConverters 是来自 RestTemplateBuilderConfigurer

public RestTemplateBuilder configure(RestTemplateBuilder builder) {
    if (this.httpMessageConverters != null) {
        builder = builder.messageConverters(this.httpMessageConverters.getConverters());
    }
    builder = addCustomizers(builder, this.restTemplateCustomizers, RestTemplateBuilder::customizers);
    builder = addCustomizers(builder, this.restTemplateRequestCustomizers, RestTemplateBuilder::requestCustomizers);
    return builder;
}

再找到 RestTemplateBuilderConfigurer 的 Bean 定义

@Bean
@Lazy
@ConditionalOnMissingBean
public RestTemplateBuilderConfigurer restTemplateBuilderConfigurer(
    ObjectProvider<HttpMessageConverters> messageConverters,
    ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
    ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) {
    RestTemplateBuilderConfigurer configurer = new RestTemplateBuilderConfigurer();
    configurer.setHttpMessageConverters(messageConverters.getIfUnique());
    configurer.setRestTemplateCustomizers(restTemplateCustomizers.orderedStream().collect(Collectors.toList()));
    configurer.setRestTemplateRequestCustomizers(
        restTemplateRequestCustomizers.orderedStream().collect(Collectors.toList()));
    return configurer;
}

得,接着找 HttpMessageConverters,这个类相当于是 HttpMessageConverter 的一个集合,同时又提供了一些额外的操作

public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>

再接着找这个 Bean 的定义,来到 HttpMessageConvertersAutoConfiguration 配置类,部分代码如下

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpMessageConverter.class)
@Conditional(NotReactiveWebApplicationCondition.class)
@AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class, JsonbAutoConfiguration.class })
@Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class,
		JsonbHttpMessageConvertersConfiguration.class })
public class HttpMessageConvertersAutoConfiguration {

	static final String PREFERRED_MAPPER_PROPERTY = "spring.mvc.converters.preferred-json-mapper";

	@Bean
	@ConditionalOnMissingBean
	public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
		return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
	}
    ...
}

可以看到 @Import 导入了 JacksonHttpMessageConvertersConfiguration,这个就是最终答案。点进去,部分代码如下

@Bean
@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class,
                          ignoredType = { "org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter", "org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
    return new MappingJackson2HttpMessageConverter(objectMapper);
}

再来看注入的这个 ObjectMapper 的 Bean 定义,配置类是 JacksonAutoConfiguration,具体的 Bean 定义是在其内部配置类中

@Bean
@Primary
@ConditionalOnMissingBean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    return builder.createXmlMapper(false).build();
}

再看 Jackson2ObjectMapperBuilder ,依然是在 JacksonAutoConfiguration 中。

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder(ApplicationContext applicationContext,
                                                       List<Jackson2ObjectMapperBuilderCustomizer> customizers) {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.applicationContext(applicationContext);
    customize(builder, customizers);
    return builder;
}

通过 customize 来设置 builder 的属性,Jackson2ObjectMapperBuilderCustomizer 是一个接口,其实现类为 StandardJackson2ObjectMapperBuilderCustomizer,其 Bean 定义如下

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
@EnableConfigurationProperties(JacksonProperties.class)
static class Jackson2ObjectMapperBuilderCustomizerConfiguration {

    @Bean
    StandardJackson2ObjectMapperBuilderCustomizer standardJacksonObjectMapperBuilderCustomizer(
        ApplicationContext applicationContext, JacksonProperties jacksonProperties) {
        return new StandardJackson2ObjectMapperBuilderCustomizer(applicationContext, jacksonProperties);
    }
    ..
}

可以看到,注入了属性配置类 JacksonProperties,而上面提到的 customize 方法具体实现如下

@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
    if (this.jacksonProperties.getDefaultPropertyInclusion() != null) {
        builder.serializationInclusion(this.jacksonProperties.getDefaultPropertyInclusion());
    }
    if (this.jacksonProperties.getTimeZone() != null) {
        builder.timeZone(this.jacksonProperties.getTimeZone());
    }
    configureFeatures(builder, FEATURE_DEFAULTS);
    configureVisibility(builder, this.jacksonProperties.getVisibility());
    configureFeatures(builder, this.jacksonProperties.getDeserialization());
    configureFeatures(builder, this.jacksonProperties.getSerialization());
    configureFeatures(builder, this.jacksonProperties.getMapper());
    configureFeatures(builder, this.jacksonProperties.getParser());
    configureFeatures(builder, this.jacksonProperties.getGenerator());
    configureDateFormat(builder);
    configurePropertyNamingStrategy(builder);
    configureModules(builder);
    configureLocale(builder);
    configureDefaultLeniency(builder);
    configureConstructorDetector(builder);
}

看到这里,一切明了了,最终的答案就在 JacksonProperties 这个属性配置类上,其定义如下,可以看到是 spring.jackson 前缀的配置,很熟悉有木有,这下终于到底了。。。

@ConfigurationProperties(prefix = "spring.jackson")
public class JacksonProperties

看了这么多代码,原来只需要在配置文件里加一下就行了,不得不感叹,spring 封装的真的是太多了,找起来真费劲。

接下来动手环节,在 application.yml 配置文件中添加如下配置

spring:
  jackson:
    deserialization:
      accept-empty-string-as-null-object: true
      accept-single-value-as-array: true

启动,打断点可以看到,这两个属性设置进来了

image-20220510103131095

RestTemplate 中多了一个,不用管,最终解析 Json 的时候使用的是第一个。

image-20220510103428406

使用IEDA的Evaluate查看一下,可以看到,第7个的反序列化特性是生效的,如果看第8个,那就是默认的false了,不过不要紧,按顺序来,只要前面的没问题就行。

image-20220510103941105

接下来再来测试之前提到的两个问题,就可以发现问题都解决了,perfect。

不过我转念一想,这样修改的话,也相当于修改了标注了 @Primary 注解的 ObjectMapper 这个 Bean ,或许也会对其他方面造成影响,而我这里明显只需要修改我这一个 RestTemplate 的配置就行了。

既然我只需要改 RestTemplate 中的反序列方式,那之前的方法就不用了,用下面的第二种方法,将影响范围降到最低。

@Bean
@ConditionalOnClass(RestTemplate.class)
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    RestTemplate restTemplate = restTemplateBuilder.build();
    restTemplate.setRequestFactory(clientHttpRequestFactory());
    for (HttpMessageConverter<?> messageConverter : restTemplate.getMessageConverters()) {
        if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
            MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) messageConverter;
            ObjectMapper mapper = mappingJackson2HttpMessageConverter.getObjectMapper();
            mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
            mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        }
    }
    return restTemplate;
}

如上,通过一个循环找出所有 MappingJackson2HttpMessageConverter(之前的截图提到了,会有两个,但是最终执行的是第一个,第二个不影响),将其属性拷贝给一个新的对象,对新的对象进行修改,这样改就可以将影响范围降低。这里如果直接对原始的对象进行修改的话,与第一种方式是一样的,所以不推荐。

不过最终我其实还是使用的第一种方法,毕竟这是个小项目,ObjectMapper 用到的地方也不太多,仅仅修改这两个反序列属性也不会对其他地方造成影响。

总结:自己动手调试一遍的收获是远远高于百度搜索的。

Spring RestTemplate调用泛型接口,别再为Map<String, String>发愁了! 本文深入探讨了Spring RestTemplate调用泛型接口时遇到的类型擦除问题,并详细介绍了使用ParameterizedTypeReference解决Map<String, String>等复杂泛型类型转换的方法。通过实际代码示例和原理分析,帮助开发者高效处理API调用中的泛型响应,提升代码安全性和可维护性。 阅读详情

相关推荐

RestTemplate实战指南:同步HTTP客户端在微服务中的精细控制与避坑

在微服务架构中,HTTP客户端是服务间通信的核心组件,其核心原理是通过封装底层网络库,提供标准化的API来发起请求与处理响应。从技术价值看,一个设计良好的HTTP客户端能显著提升系统间的交互效率与稳定性,尤其在处理高并发、超时控制与连接复用等场景时至关重要。在实际应用场景中,开发者常面临连接池配置、超时策略、异常处理与数据序列化等工程挑战。本文聚焦于Spring框架中的RestTemplate,探讨如何通过配置连接池和拦截器来实现精细化的HTTP调用控制,从而在对接外部服务与老旧系统时,确保应用的鲁棒性与可

weixin_33933118的博客 417

Jackson中DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT和DeserializationFeature. ACCEPT_EMP

文章目录DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECTDeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT总结 Jackson使用过程中的一些疑惑和跟踪。 DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT 首先先来看看这个配置项对应的JavaDoc: /** * Feature that can be

徐小陌的博客 5240

深入解析RestTemplate:Java HTTP客户端核心原理、配置优化与实战避坑指南

在微服务架构和分布式系统中,服务间通信是基础且关键的技术环节。HTTP作为最广泛的应用层协议,其客户端实现原理涉及连接管理、请求/响应序列化与超时控制等核心机制。Spring框架提供的RestTemplate采用模板方法模式,通过ClientHttpRequestFactory和HttpMessageConverter等组件,将底层HTTP库的复杂性封装,为开发者提供了简洁、统一的同步调用API,极大提升了开发效率并降低了集成成本。其价值在于与Spring生态的无缝融合,以及对存量系统的良好支持。典型应用场

weixin_30530939的博客 385

springboot场景下jackson反序列化将空字符串转Null

springboot场景下jackson反序列化将空字符串转Null

高亮博客 2123

深入解析RestTemplate:Java HTTP客户端核心原理与工程实践

HTTP客户端是微服务架构中服务间通信的基础组件,其核心原理涉及网络连接管理、协议封装与数据序列化。在Java生态中,RestTemplate作为经典的同步阻塞式客户端,通过模板方法模式固化了HTTP请求的通用流程,提供了连接池、消息转换器与拦截器等可扩展机制,有效保障了通信的可靠性与性能。其设计思想深刻影响了后续的声明式客户端Feign,并为理解响应式客户端WebClient奠定了基础。在实际工程中,合理配置连接池参数与超时策略,结合自定义错误处理器与拦截器,能够应对高并发场景下的连接泄漏与异常处理挑战。

weixin_34323858的博客 353

SpringBoot整合Jackson-dataformat-xml:优雅处理XML请求与响应实战

本文详细介绍了在SpringBoot项目中整合Jackson-dataformat-xml模块,以优雅处理XML请求与响应的实战方法。通过引入依赖、配置注解及使用XmlMapper,开发者可以高效实现Java对象与XML数据的转换,轻松应对传统系统对接中的XML参数处理需求,提升开发效率与代码可维护性。

weixin_29268331的博客 236

Jackson解析json配置

今天对接第三方渠道接口时发现一个问题,json字符串中如果有相关属性字段为对象json字符串的时候,此时如果这个返回的这个对象字段为“”,采用Jackson将json转对象的时候会提示“空字符串不能转换为具体对象”的错误,后来找到解决办法如下:(1)还是采用Jackson的解析方式,设置其反序列化的属性为:ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,mapper.conf...

yinbenyang的专栏 5274

Spring Boot项目里RestTemplate遇到text/html响应报错?一个Bean配置搞定所有非标准JSON接口

本文详细解析了Spring Boot项目中RestTemplate处理非标准JSON响应(如text/html)时遇到的常见问题,并提供了一个万能Bean配置方案。通过扩展HttpMessageConverter支持的媒体类型,使RestTemplate能够优雅处理各种非标准接口响应,包括text/html、text/plain等格式,有效解决'no suitable HttpMessageConverter found'报错问题。

weixin_30352191的博客 442

Jackson之ObjectMapper常用用法

ObjectMapper 是 Jackson 序列化和反序列化 JSON 和 Java 对象的核心类,提供了许多用于定制序列化和反序列化的方法和配置选项

洪晓鸿 9202

SpaceMesh Mining 教程

當參與Spacemesh的用戶在完成P盤工作後需要完成兩個部分工作:PoET+PoST,這兩部分均是在不同的規定時間完成,具體可參考後續的時間表。註意!只有PoET和PoST均完成的用戶才可以獲得收益。Spacemesh相關P盤教程:說明:1.Spacemesh每個紀元epoch周期為14天,在8月7日完成PoET的將在8月20日完成PoST,收益將在8月25日後發放(之後用戶收益將每隔14天發放一次) 後續PoET時間為8月7日+N14 **後續PoST時間為8月20日+N。

Yc Chan 的个人博客 1044

macOS Monterey官方镜像高速下载指南(附迅雷链接+兼容性清单)

本文提供macOS Monterey官方镜像的高速下载指南,重点介绍利用迅雷等工具加速下载官方安装包的有效方法。同时,详细列出了系统兼容性清单,帮助用户在下载前确认设备支持,并分享了从下载、验证到安全安装的完整流程与常见问题解决方案。

j7k8l的博客 2184

java全家桶之58: restTemplate返回list

在 Spring 中,使用 RestTemplate 获取 List 类型的结果需要特殊处理,因为 Java 的类型擦除机制使得无法直接获取List<T>类型。

leijmdas的专栏 767

Dell Precision 7920 Tower开机点不亮?别慌,看电源灯闪烁密码快速定位故障(附BIOS恢复指南)

本文详细解析了Dell Precision 7920 Tower工作站开机点不亮时的电源灯闪烁密码,帮助用户快速定位故障。从内存问题到BIOS恢复,再到CPU配置错误,提供了全面的诊断和修复指南,并附有实用的维护建议和工具推荐。

weixin_30892037的博客 484

一次由 RestTemplate 引发的 Connection reset:被 XML 截胡的请求体

只返回直接替换掉了默认列表。XML/CBOR 转换器都不存在 → 只能选 JSON →。这就是为什么生产环境()一直没事,一切到直连模式就炸。OR-Tools 的/solve服务默认只声明收 JSON。收到 133KB 的要么解析层在读完 header 后就拒绝,直接 close socket要么网关/反向代理(nginx/ingress)在 Content-Type 校验阶段就 reset这两种都不会走完 HTTP 协议正常返回 4xx,而是直接断 TCP。客户端看到的就是,而不是。

IT_Octopus的博客 395

告别Vivado自带编辑器!手把手教你用Sublime Text 4 + Icarus Verilog搭建FPGA开发环境(Windows 10/11)

本文详细介绍如何在Windows 10/11系统下,使用Sublime Text 4和Icarus Verilog搭建高效的FPGA开发环境,替代Vivado自带编辑器。通过配置语法检查、代码高亮和智能补全等功能,提升Verilog开发效率,实现专业级工作流。

weixin_30684743的博客 427

Springboot restTemplate 自定义消息转换器jackson-dataformat-xml 优先生效的问题

为了解决这个问题,需要更换一下优先级,把MappingJackson2XmlHttpMessageConverter放到解析器列表的最后一个。但是如果同时引入了jackson-dataformat-xml包的时候,你会发现比较坑爹的现象。返回的json变xml了。重新设置StringHttpMessageConverter字符集为UTF-8,解决中文乱码问题。调用第三方接口,返回中文是乱码,因此需要引入自定义消息转换器。

ByteX的博客 744

STM32F407编译下载全流程:从CubeMX到HEX烧录

嵌入式固件开发中,编译与下载是连接代码与硬件的关键闭环。其本质是将高级语言描述的逻辑,经交叉编译(ARM GCC)、链接(ELF生成)、格式转换(HEX/BIN)后,通过调试接口(SWD/UART)写入MCU Flash存储器的过程。该流程涉及工具链协同、二进制格式特性(如Intel HEX的地址映射与无元数据设计)、启动模式(System Memory Bootloader)、Flash选项字节(RDP/WRP)等底层机制,直接决定固件可运行性与安全性。典型应用场景包括STM32F407开发板调试、量产固

weixin_28840811的博客 260

Spring for Android架构设计:从客户端到服务端的完整通信方案终极指南

Spring for Android是一个强大的开源框架,它为Android开发者提供了与Spring生态系统无缝集成的完整通信解决方案。这个框架的核心优势在于简化了Android应用与后端服务之间的数据交换,让开发者能够专注于业务逻辑而不是底层的网络通信细节。😊 ## 🔍 Spring for Android架构概览 Spring for Android的架构设计采用了经典的客户端-服务

gitblog_00432的博客 1020

【异常】CNB提示The bill authorization scope cannot access the current request.

摘要:CNB API返回403错误,表明当前token的授权范围无法访问目标仓库。主要原因是:1) REPO_SLUG填写错误,导致资源地址不匹配;2) CNB_TOKEN未授权目标仓库;3) 账号账单状态异常。解决步骤:先检查REPO_SLUG是否与CNB平台仓库地址完全一致;再确认CNB_TOKEN是否绑定目标仓库且有读取权限;最后检查账号账单是否正常且覆盖目标资源。完成排查后更新配置即可解决此授权问题。

奶爸的编程之路,也就一周冷个三天 663

24年电赛H题 | 想要小车走直线,正在调角度环,但不知道怎么把角度偏航角偏差转化成电机速度输出?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 1314

RestTemplate的多级泛型和消息转换器

项目中经常会用到RestTemplate,当我们使用RestTemplate去调用其他接口的时候,当返回的是一个普通的类或者一个字符串时,那正常使用即可,但是当这个接口返回一个List时(Doc是一个实体类),你只能用字符串来接收,然后在把这个字符串转换成这个对象,使用起来非常不便。这时候你可以用ParameterizedTypeReference这个类来帮你反序列化接收到的数据,如下:

天下我有00的博客 2210

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

在Spring Boot项目中使用RestTemplate发送请求时如何处理响应?

Spring Boot中使用RestTemplate处理HTTP响应时,主要通过getForObject、getForEntity和exchange三种方法实现。getForObject适合简单场景,直接返回响应体;getForEntity提供完整响应信息(状态码、头信息等);exchange则适用于复杂类型和自定义请求。文章详细讲解了如何解析字符串响应、映射到实体类、处理集合/嵌套JSON数据,以及获取响应头和Cookie等常见场景,涵盖了90%的实际开发需求。

qq_45089709的博客 659

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【REST Clients】通过 RestTemplate 访问 Web 资源

RestTemplate是执行HTTP请求的同步客户端。它是原始的Spring REST客户端,并在底层HTTP客户端库上公开了一个简单的模板方法API。

刷刷的破壳日常 365

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

RestTemplate的converters配置

为什么80%的码农都做不了架构师?>>> ...

weixin_33896069的博客 728

使用Spring-RestTemplate时出现RestClientException的解决办法

错误解释 出现这个错误有两个不同的原因: 你所访问的API的服务器的响应类型是text/html,而restTemplate不能将这种类型转换成你要求的类的结构 如果响应类型就是application/json,那么就是缺少一个jackson的依赖 解决 第一种原因的解决办法是为RestTemplate实例添加自定义消息转换器,接收所有响应类型.下面是代码. List&lt;HttpMes...

阿瞒的博客 2万+
上一篇: HDFS 的Shell命令
下一篇: HDFS之SecondaryNameNode
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值