Reputation: 461
I am using Jackson to serialize a POJO as XML. I am using the default Serializer and bumped into an unexpected issue with strings containing numbers: they just disapear from the final XML produced by Jackson.
These are 2 samples of POJO's; as you can see they are nested (the root element is missing here):
package configuration.system;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Authentication {
private User user;
@JsonProperty("oauth2-client")
private OAuth2Client oAuth2Client;
}
package configuration.system;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.util.List;
@Data
@RequiredArgsConstructor
public class User {
private static final String GROUPS_NS = "urn:rdns:com:oammodel:system-ext";
private final String name;
private final String password;
@JacksonXmlProperty(namespace = GROUPS_NS)
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> groups;
}
package configuration.system;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import lombok.Data;
@Data
@JsonPropertyOrder({"client-id", "client-secret","groups"})
public class OAuth2Client {
@JsonCreator
public OAuth2Client(final String clientId, final String clientSecret , final List<String> groups) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.groups = groups;
}
@JacksonXmlProperty(localName = "client-id")
private final String clientId;
@JacksonXmlProperty(localName = "client-secret")
private final String clientSecret;
@JacksonXmlElementWrapper(useWrapping = false)
private final List<String> groups;
}
And all elements are correctly parsed and serialized as expected, producing this output:
<system>
<authentication>
<user>
<name>admin</name>
<password>p$Wrd</password>
<groups xmlns="urn:rdns:com:oammodel:system-ext">nfv1-admin</groups>
<groups xmlns="urn:rdns:com:oammodel:system-ext">nfv2-admin</groups>
<groups xmlns="urn:rdns:com:oammodel:system-ext">system-admin</groups>
<groups xmlns="urn:rdns:com:oammodel:system-ext">nfv3-admin</groups>
</user>
<oauth-client>
<client-id>clientId</client-id>
<client-secret>acf-feb-eb-b-acb</client-secret>
<groups>scopes/system.read</groups>
<groups>scopes/system.write</groups>
</oauth-client>
</authentication>
</system>
But when I wrote a TC to check that the serialized content matched the expected XML failed, reason being that:
but, as you can see, the resulting strings from the serialization are, precisely, those set above but missing numbers. The code to serialize is as follows:
protected Optional<String> serializeInternal(final SerializationOutput outputFormat) {
final XmlMapper xmlMapper = new XmlMapper();
if (outputFormat == SerializationOutput.FORMATTED) {
xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
xmlMapper.getFactory().getXMLInputFactory().setProperty(WstxInputProperties.P_RETURN_NULL_FOR_DEFAULT_NAMESPACE,
true);
xmlMapper.getFactory().getXMLOutputFactory().setProperty(P_AUTOMATIC_NS_PREFIX, NO_NS_PREFIX);
try {
return Optional.of(xmlMapper.writeValueAsString(this).replaceAll(":*[0-9]+:*", ""));
} catch (JsonProcessingException e) {
log.error("Serialization of {} as XML failed", this.getClass().getName());
return Optional.empty();
}
}
I have tried several serialization properties without any result, some of them should not produce any result, I know, but just for trying:
I'd like to avoid coming up with a customized serializer just for this, because those strings do not have anything I should handle in a special way during serialization, and this I guess should come out of the box. Any hint is appreciated, since I don't know what else to check. Thanks.
This is added after the answer by @user3696953. The original 'problem' was the attempt to remove the namespace prefixes. As you can see, the element is namespaced. This is achieved with the @JacksonXmlProperty(namespace = GROUPS_NS)
annotation in the groups
field of the User POJO.
With that, the serialization yields:
<user>
<name>admin</name>
<password>p4$W0rd</password>
<wstxns1:groups xmlns:wstxns1="urn:rdns:com:oammodel:system-ext">nfv1-admin</wstxns1:groups>
<wstxns2:groups xmlns:wstxns2="urn:rdns:com:oammodel:system-ext">nfv2-admin</wstxns2:groups>
<wstxns3:groups xmlns:wstxns3="urn:rdns:com:oammodel:system-ext">system-admin</wstxns3:groups>
<wstxns4:groups xmlns:wstxns4="urn:rdns:com:oammodel:system-ext">nfv3-admin</wstxns4:groups>
</user>
Then I found the way to disable the default namespace prefixing, which is not directly supported by Jackson, by the way, but has to be directly set in the Output factory using a Stax API property. This is the purpose of the line xmlMapper.getFactory().getXMLOutputFactory().setProperty(P_AUTOMATIC_NS_PREFIX, NO_NS_PREFIX);
in the serialization code, where P_AUTOMATIC_NS_PREFIX
holds the name of the Stax property to control the automatic prefix (org.codehaus.stax2.automaticNsPrefix) and NO_NS_PREFIX
is just "":
protected Optional<String> serializeInternal(final SerializationOutput outputFormat) {
final XmlMapper xmlMapper = new XmlMapper();
if (outputFormat == SerializationOutput.FORMATTED) {
xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
xmlMapper.getFactory().getXMLInputFactory().setProperty(WstxInputProperties.P_RETURN_NULL_FOR_DEFAULT_NAMESPACE,
true);
xmlMapper.getFactory().getXMLOutputFactory().setProperty(P_AUTOMATIC_NS_PREFIX, NO_NS_PREFIX);
try {
return Optional.of(xmlMapper.writeValueAsString(this));
} catch (JsonProcessingException e) {
log.error(
"Serialization of {} as XML failed" + System.lineSeparator() + "Cause is: {}"
+ System.lineSeparator() + "Message: {}",
this.getClass().getName(), e.getCause(), e.getMessage());
return Optional.empty();
}
}
Afther this the serialization results in:
<user>
<name>admin</name>
<password>p4$W0rd</password>
<1:groups xmlns:1="urn:rdns:com:oammodel:system-ext">nfv1-admin</1:groups>
<2:groups xmlns:2="urn:rdns:com:oammodel:system-ext">nvf2-admin</2:groups>
<3:groups xmlns:3="urn:rdns:com:oammodel:system-ext">system-admin</3:groups>
<4:groups xmlns:4="urn:rdns:com:oammodel:system-ext">nfv3-admin</4:groups>
</user>
Closer, but not yet there. The regex to replace the numbers was the problem cusing the original question (my blindness), but the bottom question here is how to get rid of the sequencing numbers of elements present multiple times?
Upvotes: 0
Views: 1770
Reputation: 195
Your not getting any numbers because on this line below you are using a regex expression to replace all instances of numbers with no character instead
return Optional.of(xmlMapper.writeValueAsString(this).replaceAll(":*[0-9]+:*", ""));
I don't know what your intent was for using this replaceAll() method but, you need to resolve the .replaceAll(":*[0-9]+:*", "")
method to be removed or replaced with whatever it is you were actually trying to accomplish.
Upvotes: 1