-1

The question is quite simple: From this:

{
"categoryId":"some_id",    
"properties": {
            "id": "braja_de_nana",
            "displayName": "test",
            "longDescription": "<p>TESTE</p>",
            "active": true,
            "attributes": [
                {
                  "name": "made",
                  "value": "THIS_BECOMES_A_NODE_VALUE",
                  "property": "THIS_BECOMES_A_NODE_NAME"
                },
                {
                  "name": "made",
                  "value": "THIS_BECOMES_A_NODE_VALUE_2",
                  "property": "THIS_BECOMES_A_NODE_NAME_2"
                }
            ]
        }
}

UPDATE

This should be the result:

It means that every array element of 'attributes' should become a new root node.

set 'property' from 'attributes' as the object node name. set 'value' from 'attributes' as the object node value.

{
    "categoryId":"some_id",
    "THIS_BECOMES_A_NODE_VALUE":"THIS_BECOMES_A_NODE_NAME",
    "THIS_BECOMES_A_NODE_NAME_2":"THIS_BECOMES_A_NODE_VALUE_2"
    "properties": {
                "id": "braja_de_nana",
                "displayName": "test",
                "longDescription": "<p>TESTE</p>",
                "active": true
            }
    }

This is a challenge for me. I can set new nodes into the root node. Already got a map from 'attributes' and then tried to iterate them with forEach in order to put the result into one single node, but instead as shown I have to take the 'property' set it to the object name´s key, then get the value and set to its value.

UPDATE 2

 @Override
    public String toOccProductDTO(ProcessProductDTO processProductDTO) throws JsonProcessingException {
        OccProductDTO occProductDTO = OccProductDTO.builder()
                .categoryId(processProductDTO.getCategoryId())
                .productType(processProductDTO.getCategoryId())
                .properties(toOccProductPropertiesDTO(processProductDTO))
                .build();
        toOccProductPropertiesDTO(processProductDTO);
        String tree = mapper.writeValueAsString(occProductDTO);

        JsonNode root = mapper.readTree(tree);
        JsonNode attributesNodeArray = ((ObjectNode) root.get("properties"))
                .remove("p_specs");

        Iterator<JsonNode> arrayNodes = attributesNodeArray.iterator();

        while (arrayNodes.hasNext()) {
            JsonNode node = arrayNodes.next();
            root = ((ObjectNode)root).set(node.get("value").asText(), node.get("property"));
        }

        System.out.println(root.toPrettyString());
        return null;
    }

I got an: arrayNodes: Collection$EmptyIterator at that line. Am I doing something wrong?

E_net4
  • 27,810
  • 13
  • 101
  • 139
  • In your second update you have `.remove("p_specs")` instead of `.remove("attributes")`. The node `"p_specs"` is missing in your input JSON. – Oboe Apr 11 '22 at 17:56

2 Answers2

2

If you are trying to the attributes to the root node, you can remove that node and add its fields to the root.

The "attributes" node is an array with length 1, so you have to get the first element of the array to get the attribute fields.

ObjectMapper mapper = new ObjectMapper();

JsonNode root = mapper.readTree(json);

JsonNode attributesNodeArray = ((ObjectNode) root.get("properties"))
        .remove("attributes");

JsonNode attributesNode = attributesNodeArray.get(0);

Iterator<String> fieldNames = attributesNode.fieldNames();

while (fieldNames.hasNext()) {
    String name = fieldNames.next();
    root = ((ObjectNode)root).set(name, attributesNode.get(name));
}

System.out.println(root.toPrettyString());

Output:

{
  "categoryId" : "some_id",
  "properties" : {
    "id" : "braja_de_nana",
    "displayName" : "test",
    "longDescription" : "<p>TESTE</p>",
    "active" : true
  },
  "name" : "made",
  "value" : "some value",
  "property" : "some_value"
}

UPDATE

For the updated question, you can do the following:

ObjectMapper mapper = new ObjectMapper();

JsonNode root = mapper.readTree(json);

JsonNode attributesNodeArray = ((ObjectNode) root.get("properties"))
        .remove("attributes");

Iterator<JsonNode> arrayNodes = attributesNodeArray.iterator();

while (arrayNodes.hasNext()) {
    JsonNode node = arrayNodes.next();
    root = ((ObjectNode)root).set(node.get("value").asText(), node.get("property"));
}

System.out.println(root.toPrettyString());

Output:

{
  "categoryId" : "some_id",
  "properties" : {
    "id" : "braja_de_nana",
    "displayName" : "test",
    "longDescription" : "<p>TESTE</p>",
    "active" : true
  },
  "THIS_BECOMES_A_NODE_VALUE" : "THIS_BECOMES_A_NODE_NAME",
  "THIS_BECOMES_A_NODE_VALUE_2" : "THIS_BECOMES_A_NODE_NAME_2"
}
Oboe
  • 2,643
  • 2
  • 9
  • 17
  • Thank you for answering! This is almost the result I need. But there are two points: 1. 'attributes' can have N objects with those three fields (name, value and property) 2. after removing I have to set 'property' as objects node name and 'value' as its value. Is it possible to do that in java? Thank you in advance – Binyamin Ely Apr 11 '22 at 17:03
  • while debbuging your code, I get the expected result if I have only one object in 'attributes'. Could I use Json array or something like that? – Binyamin Ely Apr 11 '22 at 17:05
  • Update your question with the same JSON input and output you need (with more than one attribute), and I'll try to update my answer accordingly. – Oboe Apr 11 '22 at 17:06
  • Thanks! I have updated now. Hope that it´s clearer now. – Binyamin Ely Apr 11 '22 at 17:18
  • attributesNode always gets null no mather what I pass as param – Binyamin Ely Apr 11 '22 at 17:32
  • In the answer, I use the JSON posted in the question (a simple copy/paste), and the output is what I get when running the code (which is equal to the desired JSON). – Oboe Apr 11 '22 at 17:36
  • I´m gonna post my whole method because it´s still not working for me. – Binyamin Ely Apr 11 '22 at 17:52
  • 'p_specs' is a list of attributes that get parsed before handling the json. – Binyamin Ely Apr 11 '22 at 17:55
  • Till now, I just had to parse into JSON using 'writeValueAsString' because my objects were static so to speak. However in the last minute they said that 'attributes' (input) should become a root node (p_specs in the output to the other micro service). The problem is I can´t create another fields for this new 'attributes' because its size is random. It could be 1 or N attributes so I´m not able to forsee how many fields in my java object I ´m gonna need. Therefore I decided for this approach in Json. – Binyamin Ely Apr 11 '22 at 17:58
  • it seems that the .remove() is erasing the whole array and its objects. Not only the node. – Binyamin Ely Apr 11 '22 at 18:33
0

Maybe it was more complex than expected. It turns out that I solved the problem with Oboe´s help. Although he missed some points through his implementation plus some changes I could achieve the goal.

         //Converts the parsed objects into Json String
                String tree = mapper.writeValueAsString(occProductDTO);
        
                //Reads the json string to JsonNode in order to manipulate it
                JsonNode root = mapper.readTree(tree);

//Sets the chosen node where the new nodes should be created
 JsonNode properties = root.path("properties");
        
//maps the two attribs needed
                Map<String, String> attribs = processProductDTO.getProductDTO().getAttributes().stream()
                        .collect(Collectors.toMap(AttributeDTO::getProperty, AttributeDTO::getValue));
    
    //Converts each attrib into a String list
                List<String> props = attribs.entrySet().stream()
                        .sorted(Comparator.comparing(Map.Entry<String, String>::getValue).reversed())
                        .map(Map.Entry::getKey)
                        .collect(Collectors.toList());
                List<String> names = attribs.entrySet()
                        .stream()                .sorted(Comparator.comparing(Map.Entry<String,String>::getValue).reversed())
                        .map(Map.Entry::getValue)
                        .collect(Collectors.toList());
        
    //iterates over the two lists adding the attribs to their corresponding position
                Iterator<String> arrayNodes = props.listIterator();
                Iterator<String> arrayNodes2 = names.listIterator();
        
                while (arrayNodes.hasNext()) {
                    String node = arrayNodes.next();
                    String node2 = arrayNodes2.next();
                    properties = ((ObjectNode)properties).put(node, node2);
                }
        
                return mapper.writeValueAsString(root);
            }

In the end, instead of passing a java object via @Post, I´m passing a json String by using "consumes = application/json, produces = "application.json"

That´s it!

Maybe it could be achieved and better implemented with java 8 stream, but for now it works. Suggestion to improve the code are welcome!

E_net4
  • 27,810
  • 13
  • 101
  • 139