Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56914
A full implementation should ideally use a JSON library.
-- v2: windows.web/tests: Add IJsonValueStatics::Parse() tests. windows.web: Implement IJsonValue::GetBoolean(). windows.web: Implement IJsonValue::GetNumber(). windows.web: Implement IJsonValue::GetString(). windows.web: Add error handling in IJsonValue::GetObject(). windows.web: Add error handling in IJsonValue::GetArray(). windows.web: Partially implement IJsonValueStatics::Parse().
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/tests/web.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index 649a1b066ed..625cc91407e 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -119,14 +119,14 @@ static void test_JsonValueStatics(void) hr = IActivationFactory_QueryInterface( factory, &IID_IJsonValueStatics, (void **)&json_value_statics ); ok( hr == S_OK, "got hr %#lx.\n", hr );
- hr = IJsonValueStatics_CreateStringValue( json_value_statics, NULL, (IJsonValue **)&json_value ); + hr = IJsonValueStatics_CreateStringValue( json_value_statics, NULL, &json_value ); ok( hr == S_OK, "got hr %#lx.\n", hr ); if (hr == S_OK) IJsonValue_Release( json_value ); hr = WindowsCreateString( L"Wine", wcslen( L"Wine" ), &str ); ok( hr == S_OK, "got hr %#lx.\n", hr ); hr = IJsonValueStatics_CreateStringValue( json_value_statics, str, NULL ); ok( hr == E_POINTER, "got hr %#lx.\n", hr ); - hr = IJsonValueStatics_CreateStringValue( json_value_statics, str, (IJsonValue **)&json_value ); + hr = IJsonValueStatics_CreateStringValue( json_value_statics, str, &json_value ); ok( hr == S_OK, "got hr %#lx.\n", hr ); WindowsDeleteString( str );
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/tests/web.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index 625cc91407e..28771931fec 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -96,6 +96,7 @@ static void test_JsonValueStatics(void) IJsonValueStatics *json_value_statics = (void *)0xdeadbeef; IActivationFactory *factory = (void *)0xdeadbeef; IJsonValue *json_value = (void *)0xdeadbeef; + JsonValueType json_value_type; HSTRING str; HRESULT hr; LONG ref; @@ -121,17 +122,31 @@ static void test_JsonValueStatics(void)
hr = IJsonValueStatics_CreateStringValue( json_value_statics, NULL, &json_value ); ok( hr == S_OK, "got hr %#lx.\n", hr ); - if (hr == S_OK) IJsonValue_Release( json_value ); + hr = IJsonValue_get_ValueType( json_value, NULL ); + todo_wine + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValue_get_ValueType( json_value, &json_value_type ); + todo_wine + ok( json_value_type == JsonValueType_String, "got JsonValueType %d.\n", json_value_type ); + todo_wine + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ref = IJsonValue_Release( json_value ); + ok( ref == 0, "got ref %ld.\n", ref ); hr = WindowsCreateString( L"Wine", wcslen( L"Wine" ), &str ); ok( hr == S_OK, "got hr %#lx.\n", hr ); hr = IJsonValueStatics_CreateStringValue( json_value_statics, str, NULL ); ok( hr == E_POINTER, "got hr %#lx.\n", hr ); hr = IJsonValueStatics_CreateStringValue( json_value_statics, str, &json_value ); ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValue_get_ValueType( json_value, &json_value_type ); + todo_wine + ok( json_value_type == JsonValueType_String, "got JsonValueType %d.\n", json_value_type ); + todo_wine + ok( hr == S_OK, "got hr %#lx.\n", hr ); WindowsDeleteString( str ); - ref = IJsonValue_Release( json_value ); ok( ref == 0, "got ref %ld.\n", ref ); + ref = IJsonValueStatics_Release( json_value_statics ); ok( ref == 2, "got ref %ld.\n", ref ); ref = IActivationFactory_Release( factory );
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/json_value.c | 12 ++++++++++-- dlls/windows.web/tests/web.c | 5 ----- 2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index 87b3365f199..dfd3820b915 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -120,6 +120,7 @@ struct json_value IJsonValue IJsonValue_iface; LONG ref;
+ JsonValueType json_value_type; HSTRING string_value; };
@@ -192,8 +193,14 @@ static HRESULT WINAPI json_value_GetTrustLevel( IJsonValue *iface, TrustLevel *t
static HRESULT WINAPI json_value_get_ValueType( IJsonValue *iface, JsonValueType *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct json_value *impl = impl_from_IJsonValue( iface ); + + TRACE( "iface %p, value %p\n", iface, value ); + + if (!value) return E_POINTER; + + *value = impl->json_value_type; + return S_OK; }
static HRESULT WINAPI json_value_Stringify( IJsonValue *iface, HSTRING *value ) @@ -289,6 +296,7 @@ static HRESULT WINAPI json_value_statics_CreateStringValue( IJsonValueStatics *i
impl->IJsonValue_iface.lpVtbl = &json_value_vtbl; impl->ref = 1; + impl->json_value_type = JsonValueType_String; if (FAILED(hr = WindowsDuplicateString( input, &impl->string_value ))) { free( impl ); diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index 28771931fec..a4072a92db3 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -123,12 +123,9 @@ static void test_JsonValueStatics(void) hr = IJsonValueStatics_CreateStringValue( json_value_statics, NULL, &json_value ); ok( hr == S_OK, "got hr %#lx.\n", hr ); hr = IJsonValue_get_ValueType( json_value, NULL ); - todo_wine ok( hr == E_POINTER, "got hr %#lx.\n", hr ); hr = IJsonValue_get_ValueType( json_value, &json_value_type ); - todo_wine ok( json_value_type == JsonValueType_String, "got JsonValueType %d.\n", json_value_type ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); ref = IJsonValue_Release( json_value ); ok( ref == 0, "got ref %ld.\n", ref ); @@ -139,9 +136,7 @@ static void test_JsonValueStatics(void) hr = IJsonValueStatics_CreateStringValue( json_value_statics, str, &json_value ); ok( hr == S_OK, "got hr %#lx.\n", hr ); hr = IJsonValue_get_ValueType( json_value, &json_value_type ); - todo_wine ok( json_value_type == JsonValueType_String, "got JsonValueType %d.\n", json_value_type ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); WindowsDeleteString( str ); ref = IJsonValue_Release( json_value );
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56914 --- dlls/windows.web/json_value.c | 175 +++++++++++++++++++++++++++++++++- dlls/windows.web/private.h | 1 + 2 files changed, 174 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index dfd3820b915..7b08aa0de2f 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -121,6 +121,9 @@ struct json_value LONG ref;
JsonValueType json_value_type; + HSTRING parsed_string; + double parsed_number; + boolean parsed_boolean; HSTRING string_value; };
@@ -167,6 +170,7 @@ static ULONG WINAPI json_value_Release( IJsonValue *iface )
if (!ref) { + WindowsDeleteString( impl->parsed_string ); WindowsDeleteString( impl->string_value ); free( impl ); } @@ -260,10 +264,177 @@ static const struct IJsonValueVtbl json_value_vtbl =
DEFINE_IINSPECTABLE( json_value_statics, IJsonValueStatics, struct json_value_statics, IActivationFactory_iface )
+static HRESULT validate_json( HSTRING input, JsonValueType *json_value_type, double *parsed_number, boolean *parsed_boolean ) +{ + BOOL in_string = FALSE, expect_comma = FALSE, expect_value = TRUE; + int square_brackets = 0, curly_brackets = 0; + const WCHAR *json = WindowsGetStringRawBuffer( input, NULL ); + UINT json_len = wcslen( json ); + + /* FIXME: Handle all JSON edge cases */ + + if (!wcscmp( L"null", json )) + { + *json_value_type = JsonValueType_Null; + return S_OK; + } + if (!wcscmp( L"true", json ) || !wcscmp( L"false", json )) + { + *parsed_boolean = !wcscmp( L"true", json ); + *json_value_type = JsonValueType_Boolean; + return S_OK; + } + if (json[0] == '"' && json[json_len - 1] == '"') + { + *json_value_type = JsonValueType_String; + return S_OK; + } + if (json[0] == '[' && json[json_len - 1] == ']') + { + *json_value_type = JsonValueType_Array; + } + else if (json[0] == '{' && json[json_len - 1] == '}') + { + *json_value_type = JsonValueType_Object; + } + else + { + double result = 0; + WCHAR *end; + + errno = 0; + result = wcstod( json, &end ); + + if (errno == ERANGE) return WEB_E_INVALID_JSON_NUMBER; + if (result || ( !errno && end != json )) + { + *parsed_number = result; + *json_value_type = JsonValueType_Number; + return S_OK; + } + } + + for ( int i = 0; json[i] != '\0'; i++ ) + { + char c = json[i]; + + if (c == '"' && ( i == 0 || json[i - 1] != '\' )) + { + in_string = !in_string; + + if (!in_string) + { + if (expect_value) + { + expect_value = FALSE; + expect_comma = TRUE; + } + else if (expect_comma) + { + return WEB_E_INVALID_JSON_STRING; + } + } + continue; + } + + if (in_string || isspace(c)) continue; + + switch (c) + { + case '{': + if (!expect_value) return WEB_E_INVALID_JSON_STRING; + curly_brackets++; + expect_value = TRUE; + expect_comma = FALSE; + break; + case '}': + if (!curly_brackets || expect_value) return WEB_E_INVALID_JSON_STRING; + curly_brackets--; + expect_comma = TRUE; + break; + case '[': + if (!expect_value) return WEB_E_INVALID_JSON_STRING; + square_brackets++; + expect_value = TRUE; + expect_comma = FALSE; + break; + case ']': + if (!square_brackets || expect_value) return WEB_E_INVALID_JSON_STRING; + square_brackets--; + expect_comma = TRUE; + break; + case ':': + if (!expect_comma) return WEB_E_INVALID_JSON_STRING; + expect_value = TRUE; + expect_comma = FALSE; + break; + case ',': + if (!expect_comma) return WEB_E_INVALID_JSON_STRING; + expect_value = TRUE; + expect_comma = FALSE; + break; + default: + if (!expect_value) return WEB_E_INVALID_JSON_STRING; + expect_value = FALSE; + expect_comma = TRUE; + break; + } + } + + return (!in_string && !curly_brackets && !square_brackets && !expect_value) ? S_OK : WEB_E_INVALID_JSON_STRING; +} + +static HRESULT trim_string( HSTRING input, const WCHAR *string_to_remove, HSTRING *trimmed_string ) +{ + HSTRING pattern = NULL, new_string = NULL; + HRESULT hr = WindowsCreateString( string_to_remove, wcslen( string_to_remove ), &pattern ); + + if (SUCCEEDED(hr)) hr = WindowsTrimStringStart( input, pattern, &new_string ); + if (SUCCEEDED(hr)) hr = WindowsTrimStringEnd( new_string, pattern, &new_string ); + if (SUCCEEDED(hr)) hr = WindowsDuplicateString( new_string, trimmed_string ); + + WindowsDeleteString( pattern ); + WindowsDeleteString( new_string ); + return hr; +} + +static HRESULT parse_json( HSTRING json, struct json_value *impl ) +{ + HSTRING trimmed_json = NULL; + HRESULT hr = trim_string( json, L" ", &trimmed_json ); + + if (SUCCEEDED(hr) && WindowsIsStringEmpty( trimmed_json )) hr = WEB_E_INVALID_JSON_STRING; + + if (SUCCEEDED(hr)) hr = validate_json( trimmed_json, &impl->json_value_type, &impl->parsed_number, &impl->parsed_boolean ); + if (SUCCEEDED(hr)) hr = trim_string( trimmed_json, L"\"", &trimmed_json ); + if (SUCCEEDED(hr)) hr = WindowsDuplicateString( trimmed_json, &impl->parsed_string ); + + WindowsDeleteString( trimmed_json ); + return hr; +} + static HRESULT WINAPI json_value_statics_Parse( IJsonValueStatics *iface, HSTRING input, IJsonValue **value ) { - FIXME( "iface %p, input %s, value %p stub!\n", iface, debugstr_hstring( input ), value ); - return E_NOTIMPL; + struct json_value *impl; + HRESULT hr; + + FIXME( "iface %p, input %s, value %p semi-stub\n", iface, debugstr_hstring( input ), value ); + + if (!value) return E_POINTER; + if (!input) return WEB_E_INVALID_JSON_STRING; + if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; + + if (FAILED(hr = parse_json( input, impl ))) + { + free( impl ); + return hr; + } + impl->IJsonValue_iface.lpVtbl = &json_value_vtbl; + impl->ref = 1; + + *value = &impl->IJsonValue_iface; + TRACE( "created IJsonValue %p.\n", *value ); + return S_OK; }
static HRESULT WINAPI json_value_statics_TryParse( IJsonValueStatics *iface, HSTRING input, IJsonValue **result, boolean *succeeded ) diff --git a/dlls/windows.web/private.h b/dlls/windows.web/private.h index 393affd61e1..d5f0e76d641 100644 --- a/dlls/windows.web/private.h +++ b/dlls/windows.web/private.h @@ -21,6 +21,7 @@ #define __WINE_WINDOWS_WEB_PRIVATE_H
#include <stdarg.h> +#include <errno.h>
#define COBJMACROS #include "windef.h"
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/json_value.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index 7b08aa0de2f..8ee3e5b9926 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -233,7 +233,13 @@ static HRESULT WINAPI json_value_GetBoolean( IJsonValue *iface, boolean *value )
static HRESULT WINAPI json_value_GetArray( IJsonValue *iface, IJsonArray **value ) { + struct json_value *impl = impl_from_IJsonValue( iface ); + FIXME( "iface %p, value %p stub!\n", iface, value ); + + if (!value) return E_POINTER; + if (impl->json_value_type != JsonValueType_Array) return E_ILLEGAL_METHOD_CALL; + return E_NOTIMPL; }
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/json_value.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index 8ee3e5b9926..698f1384c7c 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -245,7 +245,13 @@ static HRESULT WINAPI json_value_GetArray( IJsonValue *iface, IJsonArray **value
static HRESULT WINAPI json_value_GetObject( IJsonValue *iface, IJsonObject **value ) { + struct json_value *impl = impl_from_IJsonValue( iface ); + FIXME( "iface %p, value %p stub!\n", iface, value ); + + if (!value) return E_POINTER; + if (impl->json_value_type != JsonValueType_Object) return E_ILLEGAL_METHOD_CALL; + return E_NOTIMPL; }
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/json_value.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index 698f1384c7c..5eb58790304 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -215,8 +215,14 @@ static HRESULT WINAPI json_value_Stringify( IJsonValue *iface, HSTRING *value )
static HRESULT WINAPI json_value_GetString( IJsonValue *iface, HSTRING *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct json_value *impl = impl_from_IJsonValue( iface ); + + TRACE( "iface %p, value %p\n", iface, value ); + + if (impl->json_value_type != JsonValueType_String) return E_ILLEGAL_METHOD_CALL; + if (!value) return E_POINTER; + + return WindowsDuplicateString( impl->parsed_string, value ); }
static HRESULT WINAPI json_value_GetNumber( IJsonValue *iface, DOUBLE *value )
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/json_value.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index 5eb58790304..07eb3034a2c 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -227,8 +227,15 @@ static HRESULT WINAPI json_value_GetString( IJsonValue *iface, HSTRING *value )
static HRESULT WINAPI json_value_GetNumber( IJsonValue *iface, DOUBLE *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct json_value *impl = impl_from_IJsonValue( iface ); + + TRACE( "iface %p, value %p\n", iface, value ); + + if (impl->json_value_type != JsonValueType_Number) return E_ILLEGAL_METHOD_CALL; + if (!value) return E_POINTER; + + *value = impl->parsed_number; + return S_OK; }
static HRESULT WINAPI json_value_GetBoolean( IJsonValue *iface, boolean *value )
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/json_value.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index 07eb3034a2c..1e5522eaba5 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -240,8 +240,15 @@ static HRESULT WINAPI json_value_GetNumber( IJsonValue *iface, DOUBLE *value )
static HRESULT WINAPI json_value_GetBoolean( IJsonValue *iface, boolean *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct json_value *impl = impl_from_IJsonValue( iface ); + + TRACE( "iface %p, value %p\n", iface, value ); + + if (impl->json_value_type != JsonValueType_Boolean) return E_ILLEGAL_METHOD_CALL; + if (!value) return E_POINTER; + + *value = impl->parsed_boolean; + return S_OK; }
static HRESULT WINAPI json_value_GetArray( IJsonValue *iface, IJsonArray **value )
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/windows.web/tests/web.c | 173 ++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index a4072a92db3..63ceb48e373 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -51,7 +51,7 @@ static void test_JsonObjectStatics(void) IActivationFactory *factory = (void *)0xdeadbeef; IInspectable *inspectable = (void *)0xdeadbeef; IJsonObject *json_object = (void *)0xdeadbeef; - HSTRING str; + HSTRING str = NULL; HRESULT hr; LONG ref;
@@ -90,6 +90,122 @@ static void test_JsonObjectStatics(void) ok( ref == 1, "got ref %ld.\n", ref ); }
+#define check_json( json_value_statics, json, expected_json_value_type, valid ) check_json_( __LINE__, json_value_statics, json, expected_json_value_type, valid ) +static void check_json_( unsigned int line, IJsonValueStatics *json_value_statics, const WCHAR *json, JsonValueType expected_json_value_type, boolean valid ) +{ + IJsonObject *json_object = (void *)0xdeadbeef; + IJsonArray *json_array = (void *)0xdeadbeef; + IJsonValue *json_value = (void *)0xdeadbeef; + HSTRING str = NULL, parsed_str = NULL, empty_space = NULL; + boolean parsed_boolean, expected_boolean; + JsonValueType json_value_type; + DOUBLE parsed_num; + HRESULT hr; + LONG ref; + int res; + + hr = WindowsCreateString( json, wcslen( json ), &str ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &json_value ); + if (!valid) + { + if (expected_json_value_type == JsonValueType_Number) + ok_(__FILE__, line)( hr == WEB_E_INVALID_JSON_NUMBER, "got hr %#lx.\n", hr ); + else + ok_(__FILE__, line)( hr == WEB_E_INVALID_JSON_STRING, "got hr %#lx.\n", hr ); + + WindowsDeleteString( str ); + return; + } + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValue_get_ValueType( json_value, &json_value_type ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + ok_(__FILE__, line)( json_value_type == expected_json_value_type, "got json_value_type %d.\n", json_value_type ); + + switch (expected_json_value_type) + { + case JsonValueType_Null: + hr = IJsonValue_GetString( json_value, NULL ); + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonValue_GetString( json_value, &parsed_str ); + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + WindowsDeleteString( parsed_str ); + + hr = IJsonValue_GetNumber( json_value, NULL ); + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonValue_GetNumber( json_value, &parsed_num ); + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + + hr = IJsonValue_GetBoolean( json_value, NULL ); + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonValue_GetBoolean( json_value, &parsed_boolean ); + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + + hr = IJsonValue_GetArray( json_value, NULL ); + ok_(__FILE__, line)( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValue_GetArray( json_value, &json_array ); + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + if (hr == S_OK) IJsonArray_Release( json_array ); + + hr = IJsonValue_GetObject( json_value, NULL ); + ok_(__FILE__, line)( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValue_GetObject( json_value, &json_object ); + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + if (hr == S_OK) IJsonObject_Release( json_object ); + break; + case JsonValueType_Boolean: + hr = WindowsCreateString( L" ", wcslen( L" " ), &empty_space ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + hr = WindowsTrimStringStart( str, empty_space, &parsed_str ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + hr = WindowsTrimStringEnd( parsed_str, empty_space, &parsed_str ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + expected_boolean = !wcscmp( L"true", WindowsGetStringRawBuffer( parsed_str, NULL ) ); + + hr = IJsonValue_GetBoolean( json_value, NULL ); + ok_(__FILE__, line)( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValue_GetBoolean( json_value, &parsed_boolean ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + ok_(__FILE__, line)( parsed_boolean == expected_boolean, "boolean mismatch, got %d, expected %d.\n", parsed_boolean, expected_boolean ); + break; + case JsonValueType_Number: + parsed_num = 0xdeadbeef; + hr = IJsonValue_GetNumber( json_value, NULL ); + ok_(__FILE__, line)( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValue_GetNumber( json_value, &parsed_num ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + ok_(__FILE__, line)( parsed_num != 0xdeadbeef, "failed to get parsed_num\n" ); + break; + case JsonValueType_String: + hr = IJsonValue_GetString( json_value, NULL ); + ok_(__FILE__, line)( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValue_GetString( json_value, &parsed_str ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + hr = WindowsCompareStringOrdinal( str, parsed_str, &res ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + ok_(__FILE__, line)( res != 0, "got same HSTRINGS str = %s, parsed_str = %s.\n", wine_dbgstr_hstring( str ), wine_dbgstr_hstring( parsed_str ) ); + break; + case JsonValueType_Array: + hr = IJsonValue_GetArray( json_value, &json_array ); + todo_wine + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + if (hr == S_OK) IJsonArray_Release( json_array ); + break; + case JsonValueType_Object: + hr = IJsonValue_GetObject( json_value, &json_object ); + todo_wine + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + if (hr == S_OK) IJsonObject_Release( json_object ); + break; + } + + WindowsDeleteString( empty_space ); + WindowsDeleteString( parsed_str ); + WindowsDeleteString( str ); + ref = IJsonValue_Release( json_value ); + ok_(__FILE__, line)( ref == 0, "got ref %ld.\n", ref ); +} + static void test_JsonValueStatics(void) { static const WCHAR *json_value_statics_name = L"Windows.Data.Json.JsonValue"; @@ -97,7 +213,8 @@ static void test_JsonValueStatics(void) IActivationFactory *factory = (void *)0xdeadbeef; IJsonValue *json_value = (void *)0xdeadbeef; JsonValueType json_value_type; - HSTRING str; + HSTRING str = NULL; + const WCHAR *json; HRESULT hr; LONG ref;
@@ -142,6 +259,58 @@ static void test_JsonValueStatics(void) ref = IJsonValue_Release( json_value ); ok( ref == 0, "got ref %ld.\n", ref );
+ hr = IJsonValueStatics_Parse( json_value_statics, NULL, &json_value ); + ok( hr == WEB_E_INVALID_JSON_STRING, "got hr %#lx.\n", hr ); + hr = WindowsCreateString( L"Wine", wcslen( L"Wine" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &json_value ); + ok( hr == WEB_E_INVALID_JSON_STRING, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + + /* Valid JSON */ + + json = L"null"; + check_json( json_value_statics, json, JsonValueType_Null, TRUE ); + json = L"false"; + check_json( json_value_statics, json, JsonValueType_Boolean, TRUE ); + json = L" true "; + check_json( json_value_statics, json, JsonValueType_Boolean, TRUE ); + json = L""true""; + check_json( json_value_statics, json, JsonValueType_String, TRUE ); + json = L" 9.22 "; + check_json( json_value_statics, json, JsonValueType_Number, TRUE ); + json = L" "Wine""; + check_json( json_value_statics, json, JsonValueType_String, TRUE ); + json = L"["Wine", "Linux"]"; + check_json( json_value_statics, json, JsonValueType_Array, TRUE ); + json = L"\ + {\ + "Wine": "The Wine Project",\ + "Linux": ["Arch", "BTW"]\ + }"; + check_json( json_value_statics, json, JsonValueType_Object, TRUE ); + + /* Invalid JSON */ + + json = L"True"; + check_json( json_value_statics, json, JsonValueType_Boolean, FALSE ); + json = L"1.7976931348623158e+3080"; + check_json( json_value_statics, json, JsonValueType_Number, FALSE ); + json = L"2.2250738585072014e-3080"; + check_json( json_value_statics, json, JsonValueType_Number, FALSE ); + json = L" "Wine":"; + check_json( json_value_statics, json, JsonValueType_String, FALSE ); + json = L"["Wine" "Linux"]"; + check_json( json_value_statics, json, JsonValueType_Array, FALSE ); + json = L"\ + {\ + "Wine": "The Wine Project",\ + "Linux": ["Arch", "BTW"]\ + "; + check_json( json_value_statics, json, JsonValueType_Object, FALSE ); + ref = IJsonValueStatics_Release( json_value_statics ); ok( ref == 2, "got ref %ld.\n", ref ); ref = IActivationFactory_Release( factory );
**v2** - Use a more appropriate variable name in `trim_string`
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
- return (!in_string && !curly_brackets && !square_brackets && !expect_value) ? S_OK : WEB_E_INVALID_JSON_STRING;
+}
+static HRESULT trim_string( HSTRING input, const WCHAR *string_to_remove, HSTRING *trimmed_string ) +{
- HSTRING pattern = NULL, new_string = NULL;
- HRESULT hr = WindowsCreateString( string_to_remove, wcslen( string_to_remove ), &pattern );
- if (SUCCEEDED(hr)) hr = WindowsTrimStringStart( input, pattern, &new_string );
- if (SUCCEEDED(hr)) hr = WindowsTrimStringEnd( new_string, pattern, &new_string );
- if (SUCCEEDED(hr)) hr = WindowsDuplicateString( new_string, trimmed_string );
- WindowsDeleteString( pattern );
- WindowsDeleteString( new_string );
- return hr;
You're leaking some references here, I think you don't need the last `WindowsDuplicateString` and this should work as well:
``` HSTRING pattern = NULL, new_string = NULL; HRESULT hr = WindowsCreateString( string_to_remove, wcslen( string_to_remove ), &pattern );
if (SUCCEEDED(hr)) hr = WindowsTrimStringStart( input, pattern, &new_string ); if (SUCCEEDED(hr)) hr = WindowsTrimStringEnd( new_string, pattern, trimmed_string );
WindowsDeleteString( pattern ); WindowsDeleteString( tmp ); return hr; ```
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
return S_OK;
- }
- if (!wcscmp( L"true", json ) || !wcscmp( L"false", json ))
- {
*parsed_boolean = !wcscmp( L"true", json );
*json_value_type = JsonValueType_Boolean;
return S_OK;
- }
- if (json[0] == '"' && json[json_len - 1] == '"')
- {
*json_value_type = JsonValueType_String;
return S_OK;
- }
- if (json[0] == '[' && json[json_len - 1] == ']')
- {
*json_value_type = JsonValueType_Array;
```suggestion:-0+0 FIXME( "Array parsing not implemented!\n" ); *json_value_type = JsonValueType_Array; ```
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
*parsed_boolean = !wcscmp( L"true", json );
*json_value_type = JsonValueType_Boolean;
return S_OK;
- }
- if (json[0] == '"' && json[json_len - 1] == '"')
- {
*json_value_type = JsonValueType_String;
return S_OK;
- }
- if (json[0] == '[' && json[json_len - 1] == ']')
- {
*json_value_type = JsonValueType_Array;
- }
- else if (json[0] == '{' && json[json_len - 1] == '}')
- {
*json_value_type = JsonValueType_Object;
```suggestion:-0+0 FIXME( "Object parsing not implemented!\n" ); *json_value_type = JsonValueType_Object; ```
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
DEFINE_IINSPECTABLE( json_value_statics, IJsonValueStatics, struct json_value_statics, IActivationFactory_iface )
+static HRESULT validate_json( HSTRING input, JsonValueType *json_value_type, double *parsed_number, boolean *parsed_boolean ) +{
- BOOL in_string = FALSE, expect_comma = FALSE, expect_value = TRUE;
- int square_brackets = 0, curly_brackets = 0;
- const WCHAR *json = WindowsGetStringRawBuffer( input, NULL );
- UINT json_len = wcslen( json );
```suggestion:-1+0 UINT json_len; const WCHAR *json = WindowsGetStringRawBuffer( input, &json_len ); ```
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
- WindowsDeleteString( pattern );
- WindowsDeleteString( new_string );
- return hr;
+}
+static HRESULT parse_json( HSTRING json, struct json_value *impl ) +{
- HSTRING trimmed_json = NULL;
- HRESULT hr = trim_string( json, L" ", &trimmed_json );
- if (SUCCEEDED(hr) && WindowsIsStringEmpty( trimmed_json )) hr = WEB_E_INVALID_JSON_STRING;
- if (SUCCEEDED(hr)) hr = validate_json( trimmed_json, &impl->json_value_type, &impl->parsed_number, &impl->parsed_boolean );
- if (SUCCEEDED(hr)) hr = trim_string( trimmed_json, L"\"", &trimmed_json );
- if (SUCCEEDED(hr)) hr = WindowsDuplicateString( trimmed_json, &impl->parsed_string );
Although it probably works in all cases, trimming the quotes seem specific to string values and IMO should be done in the string-specific case above.
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
DEFINE_IINSPECTABLE( json_value_statics, IJsonValueStatics, struct json_value_statics, IActivationFactory_iface )
+static HRESULT validate_json( HSTRING input, JsonValueType *json_value_type, double *parsed_number, boolean *parsed_boolean )
It does indeed some validation for array and objects but otherwise it also actually parses the json and extract the values. What about naming it `parse_json_value` and let it accept a `struct json_value *` directly?
I think you could then later implement object and array parsing easily by calling `IJsonValueStatics_Parse` recursively.
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
errno = 0;
result = wcstod( json, &end );
if (errno == ERANGE) return WEB_E_INVALID_JSON_NUMBER;
if (result || ( !errno && end != json ))
{
*parsed_number = result;
*json_value_type = JsonValueType_Number;
return S_OK;
}
- }
- for ( int i = 0; json[i] != '\0'; i++ )
- {
char c = json[i];
Unless it is important to validate the strings I would leave this for later, and return S_OK. Validation is also just a matter of actually parsing the array and objects, so implementing them would automatically end up validating the json string.
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
*json_value_type = JsonValueType_Array;
- }
- else if (json[0] == '{' && json[json_len - 1] == '}')
- {
*json_value_type = JsonValueType_Object;
- }
- else
- {
double result = 0;
WCHAR *end;
errno = 0;
result = wcstod( json, &end );
if (errno == ERANGE) return WEB_E_INVALID_JSON_NUMBER;
if (result || ( !errno && end != json ))
```suggestion:-0+0 if (!errno && end == json + json_len) ```
Rémi Bernon (@rbernon) commented about dlls/windows.web/json_value.c:
DEFINE_IINSPECTABLE( json_value_statics, IJsonValueStatics, struct json_value_statics, IActivationFactory_iface )
+static HRESULT validate_json( HSTRING input, JsonValueType *json_value_type, double *parsed_number, boolean *parsed_boolean ) +{
- BOOL in_string = FALSE, expect_comma = FALSE, expect_value = TRUE;
- int square_brackets = 0, curly_brackets = 0;
- const WCHAR *json = WindowsGetStringRawBuffer( input, NULL );
- UINT json_len = wcslen( json );
- /* FIXME: Handle all JSON edge cases */
- if (!wcscmp( L"null", json ))
Is the string guaranteed to be 0 terminated? You should probably check that length is the expected string length and use `wcsncmp` for comparison, here and everywhere below. For `wcstod` you can perhaps use `wcstold` or make a copy.
It would be more robust for later if we want to optimize things later and avoid too many allocations, by parsing the whole string in place.
Regarding using a library, I would have considered adding cJSON to libs/ but we actually need to parse WCHAR strings, which it doesn't do. And JSON should hopefully be simple enough to implement our own parser.