Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Program crashes with ordered_json, but works fine with json #4279

Closed
2 tasks
vircon32 opened this issue Jan 29, 2024 · 5 comments · Fixed by #4516
Closed
2 tasks

Program crashes with ordered_json, but works fine with json #4279

vircon32 opened this issue Jan 29, 2024 · 5 comments · Fixed by #4516
Labels
documentation solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@vircon32
Copy link

vircon32 commented Jan 29, 2024

Description

The same program (code in case 1) produces the expected result using regular nlohmann::json, but crashes using nlohmann::ordered_json. A simple variant of this program (code in case 2) produces the expected result using json, but a different result with ordered_json.

Reproduction steps

Consider this input json file ("test.json"):

{
  "number": 14,
  "structure": {
    "field1": 1,
    "field2": 2
  }
}

The 2 small programs listed into "Minimal code example" will work fine and produce the expected results when using my_json = nlohmann::json. However, if we change the type to nlohmann::ordered_json, the first program will crash and the second program will not add new_field as expected.

Expected vs. actual results

Expected result for case 1:

{
  "number": 14,
  "my_structures": {
    "structure": {
      "field1": 1
    }
  }
}

Expected result for case 2:

{
  "number": 14,
  "my_structures": {
    "structure": {
      "field1": 1,
      "field2": 2,
      "new_field": "new"
    }
  }
}

Minimal code example

// CASE 1: removing an old field
using my_json = nlohmann::json;

int main()
{
    std::ifstream InputStream( "test.json" );
    my_json Root = my_json::parse( InputStream );

    // create new level at root
    my_json& MyStructures = Root[ "my_structures" ];
        
    // move structure into that new level
    MyStructures[ "structure" ] = Root[ "structure" ];
    Root.erase( "structure" );

    // add new structure field
    MyStructures[ "structure" ].erase( "field2" );
    
    std::ofstream OutputStream( "converted.json" );
    OutputStream << std::setw( 2 ) << Root << std::endl;    
}

// CASE 2: adding a new field
using my_json = nlohmann::json;

int main()
{
    std::ifstream InputStream( "test.json" );
    my_json Root = my_json::parse( InputStream );

    // create new level at root
    my_json& MyStructures = Root[ "my_structures" ];
        
    // move structure into that new level
    MyStructures[ "structure" ] = Root[ "structure" ];
    Root.erase( "structure" );

    // add new structure field
    MyStructures[ "structure" ][ "new_field" ] = "new";
    
    std::ofstream OutputStream( "converted.json" );
    OutputStream << std::setw( 2 ) << Root << std::endl;    
}

Error messages

I am using the library as a single include, and for the crashing case an exception is being thrown at this point (line 21878):

  private:
    template < typename KeyType, detail::enable_if_t <
                   detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
    size_type erase_internal(KeyType && key)
    {
        // this erase only works for objects
        if (JSON_HEDLEY_UNLIKELY(!is_object()))
        {
    ----->   JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
        }

        return m_data.m_value.object->erase(std::forward<KeyType>(key));
    }

The exception message is: [json.exception.type_error.307] cannot use erase() with null

Compiler and operating system

Windows 10, Microsoft Visual Studio Professional 2019 Version 16.11.24

Library version

Github at commit 7efe875

Validation

@vircon32
Copy link
Author

Update: I have verified that the crash also happens when I use json.hpp from the latest release (version 3.11.3).

@gregmarr
Copy link
Contributor

// create new level at root
my_json& MyStructures = Root[ "my_structures" ];
    
// move structure into that new level
MyStructures[ "structure" ] = Root[ "structure" ];
Root.erase( "structure" );

// add new structure field
MyStructures[ "structure" ].erase( "field2" );

You can't keep references into the ordered_json across modifications to the fields of the object, as it uses a vector internally. Try this:

// create new level at root and 
// move structure into that new level
Root[ "my_structures" ][ "structure" ] = Root[ "structure" ];
Root.erase( "structure" );

// add new structure field
Root[ "my_structures" ][ "structure" ].erase( "field2" );

@vircon32
Copy link
Author

Thank you gregmarr, it does work this way. I didn't know of this limitation. Does the documentation mention this? If so, please excuse me.

@gregmarr
Copy link
Contributor

gregmarr commented Jan 29, 2024

I don't think there is a section in iterator/pointer/reference invalidation. It is mentioned in the code at https://github.com/nlohmann/json/blob/develop/include/nlohmann/json.hpp#L768 and was the cause of #2962 fixed by #2963

There is a "todo" section here:

## Iterator invalidation

@nlohmann
Copy link
Owner

Thanks @gregmarr. I marked this as a documentation issue.

@nlohmann nlohmann added this to the Release 3.11.4 milestone Dec 7, 2024
@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Dec 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants