javajacksonjackson-databindjackson2jackson-dataformat-xml

Jackson XML to JSON, ignore attributes


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.


Solution

  • 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: