Compare two JSON structures for equality.
This function recursively traverses both structures and checks that they have: * The same variable types * The same values (for primitives) * The same number of children (for objects and arrays) * The same structure (recursively)
type(json_core) :: json
type(json_value),pointer :: p1, p2
logical :: are_equal
call json%load(file='file1.json', p=p1)
call json%load(file='file2.json', p=p2)
are_equal = json%equals(p1, p2)
if (are_equal) then
write(*,*) 'Files are identical'
else
write(*,*) 'Files differ'
end if
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(json_core), | intent(inout) | :: | json | |||
| type(json_value), | pointer | :: | p1 |
first JSON structure |
||
| type(json_value), | pointer | :: | p2 |
second JSON structure |
||
| logical(kind=LK), | intent(in), | optional | :: | verbose |
if true, print debug info. [default is false] |
true if the structures are equal
recursive function json_value_equals(json, p1, p2, verbose) result(equals) implicit none class(json_core),intent(inout) :: json type(json_value),pointer :: p1 !! first JSON structure type(json_value),pointer :: p2 !! second JSON structure logical(LK) :: equals !! true if the structures are equal logical(LK),intent(in),optional :: verbose !! if true, print debug info. [default is false] integer(IK) :: n1, n2 !! number of children type(json_value),pointer :: child1, child2 !! for iterating children logical(LK) :: child_found !! for get_child calls logical(LK) :: debug !! if `verbose` is set ! Initialize equals = .false. if (present(verbose)) then debug = verbose else debug = .false. end if ! Check if both are null if (.not. associated(p1) .and. .not. associated(p2)) then equals = .true. return end if ! Check if only one is null if (.not. associated(p1) .or. .not. associated(p2)) then if (debug) write(error_unit,'(A)') 'One pointer is null, the other is not' equals = .false. return end if ! Check if variable types match if (p1%var_type /= p2%var_type) then if (debug) write(error_unit,'(A,I0,A,I0)') 'Type mismatch: p1%var_type=', & p1%var_type, ', p2%var_type=', p2%var_type equals = .false. return end if ! Compare based on type select case (p1%var_type) case (json_null) ! Both are null, already validated by type check equals = .true. case (json_logical) ! Compare logical values if (allocated(p1%log_value) .and. allocated(p2%log_value)) then equals = (p1%log_value .eqv. p2%log_value) if (.not. equals .and. debug) then write(error_unit,'(A)') 'Logical values differ' write(error_unit,'(A,L1)') ' p1: ', p1%log_value write(error_unit,'(A,L1)') ' p2: ', p2%log_value end if else if (.not. allocated(p1%log_value) .and. .not. allocated(p2%log_value)) then equals = .true. else if (debug) write(error_unit,'(A)') 'Logical allocation mismatch' equals = .false. end if case (json_integer) ! Compare integer values if (allocated(p1%int_value) .and. allocated(p2%int_value)) then equals = (p1%int_value == p2%int_value) if (.not. equals .and. debug) then write(error_unit,'(A)') 'Integer values differ' write(error_unit,'(A,I0)') ' p1: ', p1%int_value write(error_unit,'(A,I0)') ' p2: ', p2%int_value end if else if (.not. allocated(p1%int_value) .and. .not. allocated(p2%int_value)) then equals = .true. else if (debug) write(error_unit,'(A)') 'Integer allocation mismatch' equals = .false. end if case (json_real) ! Compare real values with tolerance if (allocated(p1%dbl_value) .and. allocated(p2%dbl_value)) then associate (r1 => p1%dbl_value, r2 => p2%dbl_value) ! Handle special cases: NaN, Inf, -Inf if (ieee_is_nan(r1) .and. ieee_is_nan(r2)) then equals = .true. else if (ieee_is_nan(r1) .or. ieee_is_nan(r2)) then if (debug) write(error_unit,'(A)') 'One value is NaN, the other is not' equals = .false. else if (.not. ieee_is_finite(r1) .and. .not. ieee_is_finite(r2)) then ! Both infinite - check if same sign equals = (r1 == r2) if (debug .and. .not. equals) write(error_unit,'(A)') 'Both values are infinite but have different signs' else if (ieee_is_finite(r1) .and. ieee_is_finite(r2)) then ! Both finite: equals = r1 == r2 if (.not. equals .and. debug) then write(error_unit,'(A)') 'Real values differ' write(error_unit,'(A,F24.16)') ' p1: ', r1 write(error_unit,'(A,F24.16)') ' p2: ', r2 end if else if (debug) write(error_unit,'(A)') 'One value is finite, the other is not' equals = .false. end if end associate else if (.not. allocated(p1%dbl_value) .and. .not. allocated(p2%dbl_value)) then equals = .true. else if (debug) write(error_unit,'(A)') 'Real allocation mismatch' equals = .false. end if case (json_string) ! Compare string values if (allocated(p1%str_value) .and. allocated(p2%str_value)) then equals = (p1%str_value == p2%str_value) if (.not. equals .and. debug) then write(error_unit,'(A)') 'String values differ' write(error_unit,'(A,A,A)') ' p1: "', p1%str_value, '"' write(error_unit,'(A,A,A)') ' p2: "', p2%str_value, '"' end if else if (.not. allocated(p1%str_value) .and. .not. allocated(p2%str_value)) then equals = .true. else if (debug) write(error_unit,'(A)') 'String allocation mismatch' equals = .false. end if case (json_array) ! Compare arrays: must have same number of elements in same order n1 = json%count(p1) n2 = json%count(p2) if (n1 /= n2) then if (debug) write(error_unit,'(A,I0,A,I0)') 'Array size mismatch: n1=', n1, ', n2=', n2 equals = .false. return end if ! Compare each element child1 => p1%children child2 => p2%children do while (associated(child1) .and. associated(child2)) ! Recursively compare children if (.not. json%equals(child1, child2)) then if (debug) write(error_unit,'(A)') 'Array element mismatch' equals = .false. return end if child1 => child1%next child2 => child2%next end do ! If we got here, all elements matched equals = .true. case (json_object) ! Compare objects: must have same keys with same values n1 = json%count(p1) n2 = json%count(p2) if (n1 /= n2) then if (debug) write(error_unit,'(A,I0,A,I0)') 'Object size mismatch: n1=', n1, ', n2=', n2 equals = .false. return end if ! For each child in p1, find matching child in p2 by name child1 => p1%children do while (associated(child1)) ! Look for child with same name in p2 nullify(child2) if (allocated(child1%name)) then call json%get_child(p2, child1%name, child2, child_found) if (.not. child_found) then ! Key not found in p2 if (debug) write(error_unit,'(A,A,A)') 'Key not found in p2: "', child1%name, '"' equals = .false. return end if ! Recursively compare values if (.not. json%equals(child1, child2)) then if (debug) write(error_unit,'(A,A,A)') 'Value mismatch for key: "', child1%name, '"' equals = .false. return end if end if child1 => child1%next end do ! If we got here, all keys and values matched equals = .true. case default ! Unknown type if (debug) write(error_unit,'(A)') 'Unknown variable type encountered' equals = .false. end select end function json_value_equals