0

My json file is like this

  {
    "Parent1": {
    "Child1":"TRUE",
    "RandomKey":"TEST-111"
   },
     "Parent2": {
      "Child1":"TRUE",
     "AnotherRandomKey":"TEST-432"
    }
  }

My goal is to extract first occurrence of Child1 from either Parent1 or Parent2 depending on whatever exists, if both exists it should just get the first.

I have the following code written in java but it is always returning false. Not sure what i am doing wrong

   final var content = Jackson.getObjectMapper().readTree(//json link);

   Stream.concat(
         Optional.ofNullable(content.at("/Parent1/Child1")).stream(),
         Optional.ofNullable(content.at("/Parent2/Child1")).stream())
        .map(JsonNode::asBoolean)
        .filter(Objects::nonNull)
        .findFirst()
        .orElse(false);

I am trying to combine both streams and just find the first occurence of Child1 and return the boolean.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
Saad
  • 915
  • 9
  • 19

1 Answers1

1

The problem is that for some reason, JsonNode.asBoolean() is implemented to be case-sensitive. Hence, parses "TRUE" as false.

Method that will try to convert value of this node to a Java boolean. JSON booleans map naturally; integer numbers other than 0 map to true, and 0 maps to false and Strings "true" and "false" map to corresponding values.

It's not very intuitive because standard JDK wrapper type Boolean performs parsing in the case-insensitive manner.

As a workaround, we can extract a String from a node, and then apply Boolean.parseBoolean.

Note: there's no need to create an Optional in order to generate a stream. In this case, Optional.ofNullable(content.at()) is an abuse of optional (for more details on the usage of optional see).

We can use Stream.of() for that purpose.

public static void main(String[] args) throws JsonProcessingException {
    String message = """
        {
            "Parent1": {
            "Child1":"TRUE",
                "RandomKey":"TEST-111"
        },
            "Parent2": {
            "Child1":"TRUE",
                "AnotherRandomKey":"TEST-432"
        }
        }""";
    
    ObjectMapper mapper = new ObjectMapper();
    JsonNode content = mapper.readTree(message);
    
    boolean result = Stream.of(content.at("/Parent1/Child1"), content.at("/Parent2/Child1"))
        .filter(Objects::nonNull)
        .map(JsonNode::asText)
        .map(Boolean::parseBoolean)
        .filter(value -> value) // guarding against the case when node "Parent1" doesn't exist
        .findFirst()
        .orElse(false);

    System.out.println(result);
    
}

Output:

true
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • For some reason i am getting t.java:65: error: incompatible types: cannot infer type-variable(s) R [javac] .map(JsonNode::asText) [javac] ^ [javac] (argument mismatch; invalid method reference [javac] incompatible types: Stream cannot be converted to String) Let me retry and update you – Saad Aug 24 '22 at 18:23
  • @Saad That's the Jackson-dependency I'm using https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations/2.13.3. – Alexander Ivanchenko Aug 24 '22 at 18:25
  • I believe it is not working if input does not contain the Parent1 json, if we only provide it Parent2, i am getting false. – Saad Aug 24 '22 at 18:58
  • 1
    @Saad You're right. Fixed the code by adding a `filter()` before `findFirst()`. `filter(value -> value)` will retain `Boolean` objects with a value `true`. That would guard against the case when node `Parent1` doesn't exist. – Alexander Ivanchenko Aug 24 '22 at 19:06