[英]Spring Boot adding attribute to XML element but NOT in JSON response
我正在研究产生API
和 JSON 响应的 API。 我在响应中有一个元素,它仅在 XML 响应中需要一个属性。 此外,当值为null
时,该元素不应出现在两种格式的响应中。
期待:
XML:
<name>john</name>
<status type="text">married</status>
JSON:
"name":"john"
"status":"married"
这是我的代码:
/**
* POJO with bunch of LOMBOK annotations to avoid boiler-plate code.
*/
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@Data
public class User implements Customer, Serializable {
private static final long serialVersionUID = 1L;
private Status status;
private String name;
/**
* Matrital status of the user.
*/
@Builder
@Value
public static class Status {
@JacksonXmlText
private String maritalStatus;
@JacksonXmlProperty(isAttribute = true)
private String type = "text";
}
}
通过上述更改,我得到了正确的 XML 响应,但 JSON 响应也返回type=text
"status" : {
"maritalStatus" : "married",
"type" : "text"
}
我尝试将@JsonValue
添加到private String maritalStatus
,这解决了 JSON 响应,但它通过不向元素添加属性而破坏了 XML 响应。
有人可以帮忙吗?
可能最简单的方法是为User.Status
实现自定义序列化器并为不同类型的表示生成不同的 output。
class UserStatusJsonSerializer extends JsonSerializer<User.Status> {
@Override
public void serialize(User.Status value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (gen instanceof ToXmlGenerator) {
ToXmlGenerator toXmlGenerator = (ToXmlGenerator) gen;
serializeXml(value, toXmlGenerator);
} else {
gen.writeString(value.getMaritalStatus());
}
}
private void serializeXml(User.Status value, ToXmlGenerator toXmlGenerator) throws IOException {
toXmlGenerator.writeStartObject();
toXmlGenerator.setNextIsAttribute(true);
toXmlGenerator.writeFieldName("type");
toXmlGenerator.writeString(value.getType());
toXmlGenerator.setNextIsAttribute(false);
toXmlGenerator.writeRaw(value.getMaritalStatus());
toXmlGenerator.writeEndObject();
}
@Override
public boolean isEmpty(SerializerProvider provider, User.Status value) {
return value == null || value.getMaritalStatus() == null;
}
}
从现在开始,您可以删除额外的XML
注释并注册自定义序列化器:
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@Data
class User implements Serializable {
private static final long serialVersionUID = 1L;
private Status status;
private String name;
@Builder
@Value
@JsonSerialize(using = UserStatusJsonSerializer.class)
public static class Status {
private String maritalStatus;
private String type = "text";
}
}
简单的控制台应用程序用法如下所示:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Value;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
List<User> users = Arrays.asList(
createUser("John", "married"),
createUser("Tom", null));
ObjectMapper jsonMapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.build();
for (User user : users) {
System.out.println(jsonMapper.writeValueAsString(user));
System.out.println();
}
XmlMapper xmlMapper = XmlMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.build();
for (User user : users) {
System.out.println(xmlMapper.writeValueAsString(user));
System.out.println();
}
}
private static User createUser(String name, String maritalStatus) {
return User.builder()
.name(name)
.status(User.Status.builder()
.maritalStatus(maritalStatus)
.build())
.build();
}
}
约翰的 JSON:
{
"status" : "married",
"name" : "John"
}
汤姆的 JSON:
{
"name" : "Tom"
}
约翰的 XML:
<User>
<status type="text">married</status>
<name>John</name>
</User>
XML 用于汤姆
<User>
<name>Tom</name>
</User>
请注意,我们实现了UserStatusJsonSerializer#isEmpty
方法,该方法定义了Status
class 的empty
含义。 现在,我们需要在您的Spring Boot
应用程序中启用JsonInclude.Include.NON_EMPTY
功能。 将以下键添加到您的应用程序配置文件中:
spring.jackson.default-property-inclusion=non_empty
如果您不想全局启用包含,则可以使用@JsonInclude
注释仅对一个属性启用它。
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Status status;
也可以看看:
在 XML 中以一种方式编组 object 的解决方案,但在 JSON(不同的字段等)中的另一种方式是使用“mixins”。 一个技巧是你必须手动注册 mixin,没有什么魔法。 见下文。
混音接口:
public interface UserStatusXmlMixin {
@JsonValue(false)
@JacksonXmlText
String getStatus();
@JacksonXmlProperty(isAttribute = true)
String getType();
}
执行:
@Value
public class UserStatus implements UserStatusXmlMixin {
private String status;
@JsonValue
@Override
public String getStatus() {
return status;
}
@Override
public String getType() {
return "text";
}
/**
* Returns an unmodifiable UserStatus when status is available,
* otherwise return null. This will help to remove this object from the responses.
*/
public static UserStatus of(final String status) {
return Optional.ofNullable(status)
.map(UserStatus::new)
.orElse(null);
}
}
我还必须手动注册“mixin”。
@Configuration
public class AppJacksonModule extends SimpleModule {
private static final long serialVersionUID = -1;
private final Map<Class, Class> mixinByTarget;
/**
* Construct an AppJacksonModule.
*/
public AppJacksonModule() {
super("AppJacksonModule");
this.mixinByTarget = Map.of(
UserStatus.class, UserStatusXmlMixin.class
);
}
@Override
public void setupModule(final SetupContext context) {
super.setupModule(context);
final ObjectCodec contextOwner = context.getOwner();
if (contextOwner instanceof XmlMapper) {
mixinByTarget.forEach(context::setMixInAnnotations);
}
}
现在,如果输入参数是 null,我需要使用UserStatus.of(..)
创建 UserStatus, <status/>
不会出现在响应中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.