Skip to content

io_nats_jparse

Rick Hightower edited this page Jul 18, 2023 · 1 revision

io.nats.jparse

class diagram

Validation

The Validation class is a public class used for implementing validation logic in software applications. It provides methods and functionality to validate various types of input data or user inputs. This class serves as a utility for performing validation checks and returning appropriate results or error messages.

public static void main(String... args)

public static void main(String... args) {
    try {
        final File file = new File("./src/test/resources/validation/");
        System.out.println("Event Strict Parser");
        final boolean showPass = false;
        final boolean showFail = false;
        validateParser(file, (JsonParser) Json.builder().setStrict(true).buildEventParser(), showFail, showPass);
        System.out.println("Strict Parser");
        final JsonParser jsonParser = Json.builder().setStrict(true).build();
        validateParser(file, jsonParser, showFail, showPass);
    } catch (Throwable ex) {
        ex.printStackTrace();
    }
}

The main method defined in the io.nats.jparse.Validation class performs the following steps:

  1. It begins by trying to execute the code within a try block.

  2. It creates a new File object and assigns it the path "./src/test/resources/validation/".

  3. It prints the message "Event Strict Parser" to the console.

  4. It initializes two boolean variables, showPass and showFail, and assigns each a value of false.

  5. It calls the validateParser method with the file object and a JsonParser object created using the Json.builder() method. The JsonParser is configured to use strict parsing. The showFail and showPass variables are also passed to the validateParser method.

  6. It prints the message "Strict Parser" to the console.

  7. It creates a new JsonParser object by calling the Json.builder() method again and configuring it to use strict parsing.

  8. It calls the validateParser method again with the file object, the newly created JsonParser, and the showFail and showPass variables.

  9. If any exception or error occurs during the execution of the try block, it is caught by the catch block.

  10. If an exception or error is caught, it is printed to the console using the printStackTrace method. sequence diagram

private static void validateParser(File file, JsonParser jsonParser, boolean showFail, boolean showPass)

private static void validateParser(File file, JsonParser jsonParser, boolean showFail, boolean showPass) {
    int[] result1 = validate(file, "y_", showFail, showPass, jsonParser);
    int[] result2 = validate(file, "i_", showFail, showPass, jsonParser);
    int[] result3 = validate(file, "n_", showFail, showPass, jsonParser);
    System.out.printf("Passed Mandatory %d Failed %d \n", result1[0], result1[1]);
    System.out.printf("Passed Optional %d Failed %d \n", result2[0], result2[1]);
    System.out.printf("Passed Garbage %d Failed %d \n", result3[0], result3[1]);
}

The method validateParser is defined in the class io.nats.jparse.Validation. It takes four parameters: file, jsonParser, showFail, and showPass.

The method has the following steps:

  1. It calls the validate method with the parameters file, "y_", showFail, showPass, and jsonParser. The result is stored in an integer array result1.
  2. It calls the validate method again with the parameters file, "i_", showFail, showPass, and jsonParser. The result is stored in an integer array result2.
  3. It calls the validate method once more with the parameters file, "n_", showFail, showPass, and jsonParser. The result is stored in an integer array result3.
  4. It prints the number of passed and failed validations for the "Mandatory" category using the values in result1.
  5. It prints the number of passed and failed validations for the "Optional" category using the values in result2.
  6. It prints the number of passed and failed validations for the "Garbage" category using the values in result3.

The output will be in the following format:

Passed Mandatory <number of passed validations> Failed <number of failed validations>
Passed Optional <number of passed validations> Failed <number of failed validations>
Passed Garbage <number of passed validations> Failed <number of failed validations>

Please note that the actual values for the number of passed and failed validations will depend on the implementation of the validate method. sequence diagram

private static int[] validate(File file, String match, boolean showFail, boolean showPass, JsonParser jsonParser)

private static int[] validate(File file, String match, boolean showFail, boolean showPass, JsonParser jsonParser) {
    try {
        int pass = 0;
        int error = 0;
        for (File listFile : file.listFiles()) {
            if (listFile.getName().startsWith(match)) {
                final CharSource cs = Sources.fileSource(listFile);
                final String jsonString = cs.toString();
                //System.out.println("TESTING " + listFile);
                try {
                    RootNode root = jsonParser.parse(jsonString);
                    if (showPass) {
                        System.out.println("PASS! " + listFile);
                        System.out.println(cs);
                        System.out.println();
                    }
                    pass++;
                } catch (Exception ex) {
                    //ex.printStackTrace();
                    if (showFail) {
                        System.out.println("FAIL! " + listFile);
                        System.out.println(cs);
                        System.out.println();
                    }
                    error++;
                }
            }
        }
        return new int[] { pass, error };
    } catch (Throwable ex) {
        ex.printStackTrace();
    }
    return new int[] { -1, -1 };
}

The validate method is a static method defined in the io.nats.jparse.Validation class. Here is a step-by-step description of what the method does:

  1. It takes the following parameters as input:

    • file: A File object representing a directory containing files to validate.
    • match: A String used to match filenames in the directory.
    • showFail: A boolean indicating whether to show failure messages.
    • showPass: A boolean indicating whether to show success messages.
    • jsonParser: A JsonParser object used to parse the JSON data.
  2. The method starts by initializing two variables: pass and error to keep track of the number of successful and failed validations, respectively.

  3. It then iterates over each File object in the directory using an enhanced for loop.

  4. Inside the loop, it checks if the filename of the current File object starts with the match string.

  5. If the filename matches, it creates a CharSource object using the listFile and reads its content into a String variable jsonString.

  6. It then tries to parse the jsonString using the provided jsonParser object.

  7. If the parsing is successful, it increments the pass variable.

  8. If the showPass flag is true, it prints a success message along with the listFile and CharSource objects.

  9. If the parsing fails and an exception is thrown, it increments the error variable.

  10. If the showFail flag is true, it prints a failure message along with the listFile and CharSource objects.

  11. After iterating through all the files in the directory, the method returns an int array containing the number of successful and failed validations.

  12. If any Throwable is caught during the execution of the method, it prints the stack trace of the exception.

  13. If any exception or error occurs during the execution of the method, it returns an int array with -1 for both the successful and failed validations.

That's a step-by-step description of what the validate method does based on its provided body. sequence diagram

TestKeyLookUp

The TestKeyLookUp class is a public class that provides functionality for looking up test keys.

@Test

void testStringKey()

@Test
void testStringKey() {
    final JsonParser parser = Json.builder().build();
    final String json1 = "'1'";
    final String json2 = "{'1':{'2':1}}";
    final Node key = getJsonRoot(parser, json1).getNode();
    final ObjectNode map = getJsonRoot(parser, json2).getObjectNode();
    final Optional<Node> value = map.getNode(key);
    assertTrue(value.isPresent());
    final Node innerMap = value.get();
    assertTrue(innerMap instanceof ObjectNode);
    final ObjectNode innerObject = (ObjectNode) innerMap;
    final long v = innerObject.getLong("2");
    assertEquals(1, v);
}

The testStringKey method in the io.nats.jparse.TestKeyLookUp class is a unit test for the StringKey functionality. Here is a step-by-step description of what this method is doing:

  1. Creating a new instance of the JsonParser by calling Json.builder().build().
  2. Defining two String variables, json1 and json2, containing the JSON strings "'1'" and "{'1':{'2':1}}" respectively.
  3. Calling the getJsonRoot method, passing the JsonParser and json1 as arguments, to obtain the root node of the JSON from json1. Storing the result in a Node variable called key.
  4. Calling the getJsonRoot method again, passing the JsonParser and json2 as arguments, to obtain the root node of the JSON from json2. Storing the result in a ObjectNode variable called map.
  5. Calling the getNode method on the map object, passing the key as the argument, to retrieve the value associated with the given key. Storing the result in an Optional<Node> variable called value.
  6. Asserting that value is present (i.e., not null).
  7. Calling the get method on value to retrieve the actual Node object. Storing the result in a Node variable called innerMap.
  8. Asserting that innerMap is an instance of ObjectNode.
  9. Casting innerMap to ObjectNode and storing the result in an ObjectNode variable called innerObject.
  10. Calling the getLong method on innerObject, passing "2" as the argument, to retrieve the value associated with the key "2". Storing the result in a long variable called v.
  11. Asserting that the value of v is equal to 1. sequence diagram

Json

The Json class provides static utility methods for working with JSON data. It includes methods for parsing JSON data into various data structures, converting data structures to JSON, and scanning JSON data for tokens.

public static String niceJson(String json)

public static String niceJson(String json) {
    char[] chars = json.toCharArray();
    StringBuilder sb = new StringBuilder(chars.length);
    for (char c : chars) {
        if (c == '\'') {
            sb.append('"');
        } else if (c == '`') {
            sb.append('\\');
        } else {
            sb.append(c);
        }
    }
    return sb.toString();
}
```## Path

The `Path` class provides utility methods for working with JSON paths. It includes methods for parsing JSON paths, looking up nodes at specified paths, and converting paths to `PathNode` objects. This class is used in conjunction with other classes such as `Node`, `Json`, and `PathNode` to manipulate and traverse JSON data.
### public static Node atPath(final PathNode path, final Node rootNode) 
```java
public static Node atPath(final PathNode path, final Node rootNode) {
    Iterator<PathElement> iterator = path.iterator();
    Node node = rootNode;
    PathElement pathElement = null;
    try {
        while (iterator.hasNext()) {
            pathElement = iterator.next();
            switch(node.type()) {
                case OBJECT:
                    final ObjectNode objectNode = (ObjectNode) node;
                    final CharSequence key = pathElement.asKey().toCharSequence();
                    node = objectNode.getNode(key);
                    break;
                case ARRAY:
                    final ArrayNode arrayNode = (ArrayNode) node;
                    node = arrayNode.getNodeAt(pathElement.asIndex().intValue());
                    break;
                default:
                    if (node.isCollection()) {
                        node = node.asCollection().getNode(pathElement.asKey().toCharSequence());
                    } else {
                        throw new PathException("Looking up Path", "Path not found at " + path + " path element key " + pathElement.asKey().toString(), node.charSource(), node.rootElementToken().startIndex);
                    }
            }
        }
    } catch (Exception ex) {
        throw new IllegalStateException("Path not found at " + path + " path element index " + pathElement.value());
    }
    return node;
}

atPath Method Description

The atPath method is a static method defined in the Path class in the io.nats.jparse package. This method is used to navigate and retrieve a specific node in a JSON structure based on a given path.

Method Signature

public static Node atPath(final PathNode path, final Node rootNode)

Parameters

  • path (type: PathNode): The path to be followed to retrieve the desired node.
  • rootNode (type: Node): The root node of the JSON structure from which the desired node is to be retrieved.

Return Value

  • Node: The node found at the given path.

Method Logic

  • The method starts by obtaining an iterator for the given path.
  • It then initializes the node variable with the rootNode parameter.
  • A pathElement variable is also initialized to null.

The method then enters a try-catch block, where the main logic of the method is described:

  • Inside a while loop, the method checks if there are more elements in the path using the iterator's hasNext() method.
  • If there are more elements, it retrieves the next path element using the iterator's next() method and assigns it to the pathElement variable.
  • Based on the type of the current node, the method follows one of the three cases:
    • If the node is of type OBJECT, it casts the node to an ObjectNode, retrieves the key from the path element, and uses it to get the corresponding node from the object using the getNode(key) method. This new node is assigned to the node variable.
    • If the node is of type ARRAY, it casts the node to an ArrayNode and retrieves the node at the index specified by the path element using the getNodeAt(index) method. This new node is assigned to the node variable.
    • If none of the above cases match, it checks if the node is a collection using the isCollection() method. If it is, it retrieves the node based on the path element's key using the getNode(key) method. Otherwise, it throws a PathException indicating that the path was not found.
  • The while loop continues until all elements in the path have been processed.

If there is any exception thrown during the execution of the while loop, it is caught in the catch block:

  • The catch block throws an IllegalStateException indicating that the path was not found based on the path and pathElement parameters.

Finally, the method returns the node found at the given path.

Note: The specific implementation details of the Node and PathNode classes are not provided in this code snippet. The behavior of the method may vary depending on the actual implementation of these classes. sequence diagram

BenchMark

The BenchMark class is annotated with @State(value = Scope.Benchmark), which signifies that it represents a state object for benchmarking purposes. This class is intended to be used in benchmarking scenarios, providing a state for measuring the performance of methods or code snippets.

public static void main(String... args) throws Exception

public static void main(String... args) throws Exception {
    try {
        JsonParser fastParser = Json.builder().setStrict(false).build();
        fastParser.parse(doublesJsonData).asArray().getDoubleArray();
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    System.out.println("DONE");
    //
    //        try {
    //            long startTime = System.currentTimeMillis();
    //
    //            JsonParser fastParser = Json.builder().setStrict(false).build();
    //
    //            for (int i = 0; i < 10_500_000; i++) {
    //
    //                final ObjectNode objectNode = fastParser.parse(glossaryEvent).asObject();
    //                if (objectNode.getNode("subject").equalsContent("glossaryFeed")) {
    //                    final String id = objectNode.getStringNode("id").toUnencodedString();
    //                    final String type = objectNode.getStringNode("type").toUnencodedString();
    //                    final String description = objectNode.getStringNode("description").toUnencodedString();
    //                    final String data = Json.serializeToString(objectNode.getNode("data"));
    //
    //                    if (i % 1_000_000 == 0) {
    //                        System.out.printf("Elapsed time %s %s \n", ((System.currentTimeMillis() - startTime) / 1000.0), data);
    //                    }
    //                }
    //
    //                final JsonIterator iter = JsonIterator.parse(glossaryEvent);
    //                final Map<String, Object> map = iter.read(mapTypeRefJI);
    //                if (map.get("subject").equals("glossaryFeed")) {
    //                    final String id =  (String) map.get("id");
    //                    final String type = (String) map.get("type");
    //                    final String description = (String) map.get("description");
    //                    final String data = JsonStream.serialize(map.get("data"));
    //                    if (i % 1_000_000 == 0) {
    //                        System.out.printf("Elapsed time %s %s \n", ((System.currentTimeMillis() - startTime) / 1000.0), data);
    //                    }
    //                }
    //                final HashMap<String, Object> map = mapper.readValue(glossaryEvent, mapTypeRef);
    //                if (map.get("subject").equals("glossaryFeed")) {
    //                    final String id =  (String) map.get("id");
    //                    final String type = (String) map.get("type");
    //                    final String description = (String) map.get("description");
    //                    final String data = mapper.writeValueAsString(map.get("data"));
    //                }
    //
    //
    //            }
    //            System.out.println("Total Elapsed time " + ((System.currentTimeMillis() - startTime) / 1000.0));
    //
    //        } catch (Exception ex) {
    //            ex.printStackTrace();
    //        }
}

The main method in class io.nats.jparse.BenchMark is divided into multiple sections, each performing a different task. Let's break it down step by step:

  1. The method begins with a try-catch block to handle any exceptions that may occur during execution.

  2. Inside the try block, a JsonParser object named fastParser is created using the Json.builder() method. The setStrict(false) method is called to configure the parser to be non-strict.

  3. The fastParser object then parses the JSON data stored in the doublesJsonData variable. The parsed data is treated as an array and the getDoubleArray() method is called to retrieve an array of doubles.

  4. If an exception occurs during parsing, it is caught in the catch block and the stack trace is printed.

  5. After the catch block, the string "DONE" is printed to the console.

  6. The code is then commented out, starting from try { long startTime = System.currentTimeMillis(); and ending with System.out.println("Total Elapsed time " + ((System.currentTimeMillis() - startTime) / 1000.0)); }.

  7. In the commented out code, a startTime variable is initialized with the current system time.

  8. Inside a loop that iterates 10,500,000 times, the following actions are performed:

    a. The glossaryEvent is parsed by the fastParser object and the result is treated as an ObjectNode.

    b. If the "subject" field of the objectNode is equal to "glossaryFeed", the following fields are extracted from the objectNode: "id", "type", "description" and "data". The data field is serialized as a string using Json.serializeToString() method.

    c. If the loop index i is a multiple of 1,000,000, the elapsed time since startTime is printed to the console along with the serialized data.

    d. The glossaryEvent is parsed by JsonIterator and the result is stored in a Map<String, Object>.

    e. If the "subject" field of the map is equal to "glossaryFeed", the following fields are extracted: "id", "type", "description" and "data". The data field is serialized as a string using JsonStream.serialize() method.

    f. If the loop index i is a multiple of 1,000,000, the elapsed time since startTime is printed to the console along with the serialized data.

    g. The glossaryEvent is deserialized into a HashMap<String, Object> using an unspecified mapper object and mapTypeRef.

    h. If the "subject" field of the map is equal to "glossaryFeed", the following fields are extracted: "id", "type", "description" and "data". The data field is serialized as a string using mapper.writeValueAsString() method.

  9. Finally, the total elapsed time since startTime is calculated and printed to the console.

  10. If an exception occurs during execution, it is caught in the catch block and the stack trace is printed.

This is the step-by-step description of the main method based on its body. sequence diagram

//

// @Benchmark // public void cloudEventJsonIter(Blackhole bh) throws Exception

//
//    @Benchmark
//    public void cloudEventJsonIter(Blackhole bh) throws Exception{
//        final JsonIterator iter = JsonIterator.parse(glossaryEvent);
//        final Map<String, Object> map = iter.read(mapTypeRefJI);
//        if (map.get("subject").equals("glossaryFeed")) {
//                final String id =  (String) map.get("id");
//                final String type = (String) map.get("type");
//                final String description = (String) map.get("description");
//                final String data = JsonStream.serialize(map.get("data"));
//                bh.consume(new Object[]{id, type, data, description});
//        }
//    }
//
//    @Benchmark
//    public void cloudEventJackson(Blackhole bh) throws JsonProcessingException {
//        final HashMap<String, Object> map = mapper.readValue(glossaryEvent, mapTypeRef);
//        if (map.get("subject").equals("glossaryFeed")) {
//            final String id =  (String) map.get("id");
//            final String type = (String) map.get("type");
//            final String description = (String) map.get("description");
//            final String data = mapper.writeValueAsString(map.get("data"));
//            bh.consume(new Object[]{id, type, data, description});
//        }
//    }
//
//    @Benchmark
//    public void cloudEventJParse(Blackhole bh) throws JsonProcessingException {
//        final ObjectNode objectNode = fastParser.parse(glossaryEvent).asObject();
//        if (objectNode.getNode("subject").equalsContent("glossaryFeed")) {
//            final String id = objectNode.getStringNode("id").toUnencodedString();
//            final String type = objectNode.getStringNode("type").toUnencodedString();
//            final String description = objectNode.getStringNode("description").toUnencodedString();
//            final String data = Json.serializeToString(objectNode.getNode("data"));
//            bh.consume(new Object[]{id, type, data, description});
//        }
//    }
//
@Benchmark
public void jParseFastFloatArray(Blackhole bh) {
    bh.consume(this.fastParser.parse(doublesJsonData).asArray().getFloatArray());
}

The method jParseFastFloatArray is a benchmark method defined in the class io.nats.jparse.BenchMark. It is used to compare the performance of parsing a JSON array of floating-point numbers using a specific parser called fastParser.

Here is a step-by-step description of what the method is doing:

  1. It takes a parameter bh of type Blackhole. This is an object provided by the benchmark framework that can be used to consume the result of the benchmark, preventing the compiler from optimizing away the benchmarked code.

  2. The method executes the following code:

bh.consume(this.fastParser.parse(doublesJsonData).asArray().getFloatArray());
a. The `fastParser` parses the `doublesJsonData`, which is a JSON array of floating-point numbers.
b. The parsed result is treated as an array using the `asArray()` method.
c. The `getFloatArray()` method is called on the parsed array to get an array of floating-point numbers.
d. The result of `getFloatArray()` is consumed by the `Blackhole` object, ensuring that the benchmarked code is not optimized away.
  1. The method does not return any value.

Overall, the jParseFastFloatArray method benchmarks the parsing performance of the fastParser for a JSON array of floating-point numbers and consumes the result using the Blackhole object. sequence diagram

Clone this wiki locally