4

I have a xml message that needs to be converted to json message format. The xml message has xml schema defined that has type information for the elements. However, the resulting json has all the values as text values instead of boolean/number as mentioned in the xml schema. How do I instruct the XmlMapper() to derive the type information from the xml schema?

XmlMapper xmlMapper = new XmlMapper();
String xmlMsg = getResourceContent("test.xml");        
JsonNode node = xmlMapper.readTree(xmlMsg.getBytes());
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writeValueAsString(node);

Sample xml:

<myMessage>
     <id>333</id>
     <type>Text</type>
     <flag>true</flag>
</myMessage>

Resulting json:

{
  "id": "333",
  "type": "Text",
  "flag": "true"
}

Expected json:

{
  "id": 333,
  "type": "Text",
  "flag": true
}
Chris
  • 7,675
  • 8
  • 51
  • 101
user1573133
  • 904
  • 1
  • 9
  • 23

2 Answers2

4

If your xml schema is simple, converting "true/false" to "boolean" and "number" to "long" is enough. I have a solution.

After JsonNode node = xmlMapper.readTree(xmlMsg.getBytes());, the children of node is of type TextNode. You can travel node object, and convert TextNode to BooleanNode or LongNode if the value meet certain conditions.

Add the following method:

public static JsonNode resolveType(JsonNode jsonNode) {
    if (jsonNode instanceof ObjectNode) {
        ObjectNode objectNode = (ObjectNode) jsonNode;
        Iterator<Entry<String, JsonNode>> fields = objectNode.fields();
        while (fields.hasNext()) {
            Entry<String, JsonNode> next = fields.next();
            next.setValue(resolveType(next.getValue()));
        }
    } else if (jsonNode instanceof TextNode) {
        TextNode textNode = (TextNode) jsonNode;
        String value = textNode.textValue();
        if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
            jsonNode = BooleanNode.valueOf(Boolean.valueOf(value));
        } else if (StringUtils.isNumeric(value)) {
            jsonNode = LongNode.valueOf(Long.valueOf(value));
        }
    }
    return jsonNode;
}

Then use this method to resolve type of JsonNode node:

JsonNode node = xmlMapper.readTree(xmlMsg.getBytes());
node = resolveType(node); // <--- Add this statement to your code.
ObjectMapper jsonMapper = new ObjectMapper();
//...

The output should be the expected json:

{"id":333,"type":"Text","flag":true}

Kerie
  • 121
  • 5
  • I awarded this answer, because it is more near to what i need than the first answer. I think i could use this method as a basis and pull the information which field is of which type from the schema. Guessing the type will also not do, because there are fields that satisfy the numeric check but should be imported as strings. – Chris Nov 02 '18 at 07:00
3

I created POJO class where I declared my data types in Message.java:

public class Message {
    private Integer id;
    private String type;
    private Boolean flag;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Boolean getFlag() {
        return flag;
    }

    public void setFlag(Boolean flag) {
        this.flag = flag;
    }
}

and in your code:

//src the XML String to Parse
XmlMapper xmlMapper = new XmlMapper();
Message myMessage = xmlMapper.readValue(src, Message.class);

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(myMessage);

System.out.println(json);

the result is:

{"id":333,"type":"Text","flag":true}
BSeitkazin
  • 2,889
  • 25
  • 40
  • Yes using a POJO of course i can do it that way, but i have generic XML so i do not want to have POJOs because i dont want to care about their structure again in POJOs i already defined a schema for them. I need direct conversion between XML and generic JSON Objects and to make sure that they contain the correct dataTypes. – Chris Oct 31 '18 at 10:20
  • Somehow someone should make convertation from String to Boolean. Because `true` or `false` could be just text messages. So, you need to create POJO class, and clearly declare that it is boolean object. – BSeitkazin Oct 31 '18 at 10:23
  • that doesn't make sense to me. The information which attribute is of which datatype is included in the xml schema, so why duplicate information? As i stated, i don't need a POJO and i don't want a POJO: Extra Code is extra work, is extra maintenance etc... etc... and the original question also did not include POJOs. The solution using POJOs is trivial. – Chris Oct 31 '18 at 14:38
  • sorry, but you didn't mention about no POJO in the question description. afterthat, if we look https://stackify.com/java-xml-jackson/, see the part - Limitations of the Jackson XML Module. So, there is no way to solve your problem without POJO. – BSeitkazin Oct 31 '18 at 16:22
  • IMHO the question makes clear that the target type is `JsonNode`. Id does not mention any POJO other than that. – Chris Nov 02 '18 at 07:01