I have XML like this:
....
<dateIssuedField class="org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl" resolves-to="org.apache.xerces.jaxp.datatype.SerializedXMLGregorianCalendar">
<lexicalValue>0001-01-01T00:00:00</lexicalValue>
</dateIssuedField>
....
and I'd like to get JSON like this:
"dateIssuedField": {"0001-01-01T00:00:00"}
I've tried so many things. Unfortunately this 'class' and 'resolves-to' don't actually match any existing class. So I can't add a serializer/deserializer that matches these classes.
The simplest fix would be to get rid of all attributes. Then I'll just deal with 'lexicalValue'. But I can't even work that out.
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode node = xmlMapper.readTree(xml.getBytes());
ObjectMapper objectMapper = new ObjectMapper();
String jsonOutput = objectMapper.writeValueAsString(node);
I don't have the classes generated, so I can't use annotations to ignore the fields.
It looks like there is a MixIn technique, but I can't work out how to use it in this case.
As I commented: The desired output format
{"dateIssuedField": {"0001-01-01T00:00:00"}}
is not really/fully valid JSON...
(1) null value:
{"dateIssuedField": {"0001-01-01T00:00:00": null}}
or (2) single element array:
{"dateIssuedField": ["0001-01-01T00:00:00"]}
or just (my fav 3):
{"dateIssuedField": "0001-01-01T00:00:00"}
would be.
With in.xml
:
<myRoot>
<dateIssuedField class="foo.bar.Baz" resolves-to="com.example.Ignore">
<lexicalValue>0001-01-01T00:00:00</lexicalValue>
</dateIssuedField>
</myRoot>
(jdk:19, jackson-databind+jackson-dataformat-xml:2.14.2), we can navigate and modify the object tree as see fit.
To achieve (1):
package com.acme.jackson;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
import java.io.InputStream;
public class Demo1 {
public static void main(String[] args) {
XmlMapper xmlMapper = new XmlMapper();
ObjectMapper objectMapper = new ObjectMapper();
// xmlMapper.configure ...
try (InputStream xml = Thread.currentThread().getContextClassLoader().getResourceAsStream("in.xml")) {
// navigate the structure...:
ObjectNode root = (ObjectNode) xmlMapper.readTree(xml);
ObjectNode node = (ObjectNode) root.get("dateIssuedField");
// ..and modify to our needs:
node.remove("class");
node.remove("resolves-to"); // ...
// ...value to key:
String val = node.get("lexicalValue").textValue();
node.remove("lexicalValue");
node.put(val, (String) null);
// and verify:
System.err.println(objectMapper.writeValueAsString(root));
} catch (IOException ex) {
System.err.println("ohoh:");
System.err.println(ex.getLocalizedMessage());
}
}
}
prints:
{"dateIssuedField":{"0001-01-01T00:00:00":null}}
For (my fav 3) we can:
// ...
try (InputStream xml = Thread.currentThread().getContextClassLoader().getResourceAsStream("in.xml")) {
ObjectNode root = (ObjectNode) xmlMapper.readTree(xml);
root.replace("dateIssuedField",
new TextNode(
((ObjectNode) root.get("dateIssuedField")).get("lexicalValue").textValue()
)
);
// verify:
System.err.println(objectMapper.writeValueAsString(root));
} catch (IOException ex) { // ...
prints:
{"dateIssuedField":"0001-01-01T00:00:00"}
Even better: Map your model (of interest) as objects!
For larger models, it is worth to cope with (model/object/schema) "generation". And always an option: To model "manually".
Advantages: