exceptionfactory commented on code in PR #9929:
URL: https://github.com/apache/nifi/pull/9929#discussion_r2083237046


##########
nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializer.java:
##########
@@ -19,40 +19,98 @@
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.MapperFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 import 
com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
 import org.apache.nifi.registry.flow.RegisteredFlowSnapshot;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 /**
  * Implementation of {@link FlowSnapshotSerializer} that is Jackson's 
ObjectMapper.
  */
 public class JacksonFlowSnapshotSerializer implements FlowSnapshotSerializer {
 
+    private static final Set<String> EXCLUDE_JSON_FIELDS = 
Set.of("instanceIdentifier", "instanceGroupId");
+
     private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder()
             .serializationInclusion(JsonInclude.Include.NON_NULL)
             
.defaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL,
 JsonInclude.Include.NON_NULL))
             .annotationIntrospector(new 
JakartaXmlBindAnnotationIntrospector(TypeFactory.defaultInstance()))
             .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
false)
             .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
             .enable(SerializationFeature.INDENT_OUTPUT)
-            .addModule(new VersionedComponentModule())
             .build();
 
     @Override
     public String serialize(final RegisteredFlowSnapshot flowSnapshot) throws 
IOException {
-        return OBJECT_MAPPER.writeValueAsString(flowSnapshot);
+        final JsonNode tree = OBJECT_MAPPER.valueToTree(flowSnapshot);
+        final JsonNode normalized = normalize(tree);
+        return OBJECT_MAPPER.writeValueAsString(normalized);
     }
 
     @Override
     public RegisteredFlowSnapshot deserialize(final InputStream inputStream) 
throws IOException {
         return OBJECT_MAPPER.readValue(inputStream, 
RegisteredFlowSnapshot.class);
     }
 
+    private static JsonNode normalize(JsonNode node) {
+        if (node.isObject()) {
+            ObjectNode in = (ObjectNode) node;
+            ObjectNode out = JsonNodeFactory.instance.objectNode();
+
+            // collect and sort field names

Review Comment:
   Does the default output write field names in a non-deterministic order? I 
suppose it depends on whether the underlying Object is a Map or a bean of some 
kind?



##########
nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializer.java:
##########
@@ -19,40 +19,98 @@
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.MapperFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 import 
com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
 import org.apache.nifi.registry.flow.RegisteredFlowSnapshot;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 /**
  * Implementation of {@link FlowSnapshotSerializer} that is Jackson's 
ObjectMapper.
  */
 public class JacksonFlowSnapshotSerializer implements FlowSnapshotSerializer {
 
+    private static final Set<String> EXCLUDE_JSON_FIELDS = 
Set.of("instanceIdentifier", "instanceGroupId");
+
     private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder()
             .serializationInclusion(JsonInclude.Include.NON_NULL)
             
.defaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL,
 JsonInclude.Include.NON_NULL))
             .annotationIntrospector(new 
JakartaXmlBindAnnotationIntrospector(TypeFactory.defaultInstance()))
             .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
false)
             .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
             .enable(SerializationFeature.INDENT_OUTPUT)
-            .addModule(new VersionedComponentModule())
             .build();
 
     @Override
     public String serialize(final RegisteredFlowSnapshot flowSnapshot) throws 
IOException {
-        return OBJECT_MAPPER.writeValueAsString(flowSnapshot);
+        final JsonNode tree = OBJECT_MAPPER.valueToTree(flowSnapshot);
+        final JsonNode normalized = normalize(tree);
+        return OBJECT_MAPPER.writeValueAsString(normalized);
     }
 
     @Override
     public RegisteredFlowSnapshot deserialize(final InputStream inputStream) 
throws IOException {
         return OBJECT_MAPPER.readValue(inputStream, 
RegisteredFlowSnapshot.class);
     }
 
+    private static JsonNode normalize(JsonNode node) {
+        if (node.isObject()) {
+            ObjectNode in = (ObjectNode) node;
+            ObjectNode out = JsonNodeFactory.instance.objectNode();
+
+            // collect and sort field names
+            List<String> fieldNames = new ArrayList<>();
+            in.fieldNames().forEachRemaining(fieldNames::add);
+            Collections.sort(fieldNames);
+
+            for (String name : fieldNames) {
+                if (EXCLUDE_JSON_FIELDS.contains(name)) {
+                    // skip this field entirely
+                    continue;
+                }
+                out.set(name, normalize(in.get(name)));
+            }
+            return out;
+
+        } else if (node.isArray()) {
+            ArrayNode in = (ArrayNode) node;
+            // recursively normalize all elements
+            List<JsonNode> elems = StreamSupport.stream(in.spliterator(), 
false)
+                    .map(JacksonFlowSnapshotSerializer::normalize)
+                    .collect(Collectors.toList());
+
+            if (!elems.isEmpty()) {
+                JsonNode first = elems.get(0);
+                if (first.isObject() && first.has("identifier")) {

Review Comment:
   It would be helpful to add a comment on the intent of this sorting strategy.



##########
nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializer.java:
##########
@@ -19,40 +19,98 @@
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.MapperFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 import 
com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
 import org.apache.nifi.registry.flow.RegisteredFlowSnapshot;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 /**
  * Implementation of {@link FlowSnapshotSerializer} that is Jackson's 
ObjectMapper.
  */
 public class JacksonFlowSnapshotSerializer implements FlowSnapshotSerializer {
 
+    private static final Set<String> EXCLUDE_JSON_FIELDS = 
Set.of("instanceIdentifier", "instanceGroupId");
+
     private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder()
             .serializationInclusion(JsonInclude.Include.NON_NULL)
             
.defaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL,
 JsonInclude.Include.NON_NULL))
             .annotationIntrospector(new 
JakartaXmlBindAnnotationIntrospector(TypeFactory.defaultInstance()))
             .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
false)
             .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
             .enable(SerializationFeature.INDENT_OUTPUT)
-            .addModule(new VersionedComponentModule())
             .build();
 
     @Override
     public String serialize(final RegisteredFlowSnapshot flowSnapshot) throws 
IOException {
-        return OBJECT_MAPPER.writeValueAsString(flowSnapshot);
+        final JsonNode tree = OBJECT_MAPPER.valueToTree(flowSnapshot);
+        final JsonNode normalized = normalize(tree);
+        return OBJECT_MAPPER.writeValueAsString(normalized);
     }
 
     @Override
     public RegisteredFlowSnapshot deserialize(final InputStream inputStream) 
throws IOException {
         return OBJECT_MAPPER.readValue(inputStream, 
RegisteredFlowSnapshot.class);
     }
 
+    private static JsonNode normalize(JsonNode node) {
+        if (node.isObject()) {
+            ObjectNode in = (ObjectNode) node;
+            ObjectNode out = JsonNodeFactory.instance.objectNode();
+
+            // collect and sort field names

Review Comment:
   Can this be implemented using the `SORT_PROPERTIES_ALPHABETICALLY` feature?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to