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

sample-product json added as per the last suggestion #24

Merged
merged 2 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 59 additions & 137 deletions src/main/java/com/techatpark/sjson/JsonSchema.java
Original file line number Diff line number Diff line change
@@ -1,183 +1,105 @@
package com.techatpark.sjson;

import com.techatpark.sjson.util.NumberParser;



import java.io.IOException;
import java.io.Reader;

import java.io.IOException;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;

/**
* Json Schema that represents the schema document.
* Ref: https://json-schema.org/specification
* Represents a JSON schema document. Provides functionality to serialize
* Java objects to JSON strings.
*/
public class JsonSchema {
public final class JsonSchema {

/**
* @param reader file reader for json schema
* Constructor for JsonSchema.
*/
public JsonSchema(final Reader reader) {

public JsonSchema() {
// Constructor logic if any
}

/**
* Reads JSON as a Java Object.
* <p>
* It will return native java objects as given below based
* on JSON Data Type.
* Ref: https://www.w3schools.com/js/js_json_datatypes.asp
* <p>
* string - java.lang.String
* number - java.lang.Number
* object - java.util.Map
* array - java.util.List
* boolean - java.lang.Boolean
* null - null
* Reads JSON from a Reader and converts it into a Java Object.
* This method is not yet implemented.
*
* @param reader - file reader for json data
* @return object
* @throws IOException - throws io exception
* @param reader the Reader to read JSON data from
* @return Object representation of the read JSON
* @throws IOException if an I/O error occurs
*/
public Object read(final Reader reader) throws IOException {
return null;
throw new UnsupportedOperationException("Not yet implemented");
}

/**
* Get Json text for the Map.
* Converts a Map into its JSON string representation.
*
* @param jsonMap
* @return jsonText
* @param jsonMap the Map representing the JSON object
* @return the JSON string representation of the Map
*/
public String jsonText(final Map<String, Object> jsonMap) {
StringBuilder builder = new StringBuilder();
boolean isFirst = true;
builder.append("{");
for (Map.Entry<String, Object> entry : jsonMap.entrySet()) {
if (isFirst) {
isFirst = false;
} else {
builder.append(",");
}
// Create Key enclosed with "
builder.append("\"")
.append(escapeJsonTxt(entry.getKey()))
.append("\":"); // Create Key value separator

Object value = entry.getValue();
StringJoiner joiner = new StringJoiner(",", "{", "}");
jsonMap.forEach((final String key, final Object value) ->
joiner.add(formatEntry(key, value)));
return joiner.toString();
}

valueText(builder, value);
}
return builder.append("}").toString();
/**
* Formats a key-value pair into a JSON entry.
*
* @param key the key in the map
* @param value the value corresponding to the key
* @return formatted JSON entry
*/
private String formatEntry(final String key, final Object value) {
return String.format("\"%s\":%s", escapeJson(key), valueToJson(value));
}

/**
* Create Value in according to the Type.
* Converts a value to its JSON representation.
*
* @param builder
* @param value
* @param value the value to convert
* @return JSON representation of the value
*/
private void valueText(final StringBuilder builder, final Object value) {
private String valueToJson(final Object value) {
if (value == null) {
builder.append("null");
return "null";
} else if (value instanceof String) {
processString(builder, (String) value);
return "\"" + escapeJson((String) value) + "\"";
} else if (value instanceof Map) {
builder.append(jsonText((Map<String, Object>)
value));
return jsonText((Map<String, Object>) value);
} else if (value instanceof List) {
builder.append(jsonText((Map<String, Object>) value));
return listToJson((List<?>) value);
} else {
builder.append(value);
return value.toString();
}
}


/**
* Process String.
* Converts a list to its JSON array representation.
*
* @param builder
* @param value
* @param list the list to convert
* @return JSON array representation of the list
*/
private void processString(final StringBuilder builder,
final String value) {
builder.append("\"")
.append(escapeJsonTxt(value))
.append("\"");
private String listToJson(final List<?> list) {
StringJoiner sj = new StringJoiner(",", "[", "]");
list.forEach(obj -> sj.add(valueToJson(obj)));
return sj.toString();
}

/**
* Escape JSON Text.
* Escape quotes, \, /, \r, \n, \b, \f, \t
* and other control characters (U+0000 through U+001F).
* @param s
* @return escapeJsonTxt
*/
private String escapeJsonTxt(final String s) {
if (s == null) {
return null;
}
StringBuilder sb = new StringBuilder();
escape(s, sb);
return sb.toString();
}

/**
* Escape Text.
* @param s - Must not be null.
* @param sb
* Escapes special characters in a JSON string.
*
* @param text the string to escape
* @return escaped JSON string
*/
private void escape(final String s, final StringBuilder sb) {
final int len = s.length();
for (int i = 0; i < len; i++) {
char ch = s.charAt(i);
switch (ch) {
case '"':
sb.append("\\\"");
break;
case '\\':
sb.append("\\\\");
break;
case '\b':
sb.append("\\b");
break;
case '\f':
sb.append("\\f");
break;
case '\n':
sb.append("\\n");
break;
case '\r':
sb.append("\\r");
break;
case '\t':
sb.append("\\t");
break;
case '/':
sb.append("\\/");
break;
default:
//Reference: http://www.unicode.org/versions/Unicode5.1.0/
if ((ch >= '\u0000' && ch <= '\u001F')
|| (ch >= '\u007F' && ch <= '\u009F')
|| (ch >= '\u2000' && ch <= '\u20FF')) {
String ss = Integer.toHexString(ch);
sb.append("\\u");
for (int k = 0; k < NumberParser.NUMBER_FOUR
- ss.length(); k++) {
sb.append('0');
}
sb.append(ss.toUpperCase());
} else {
sb.append(ch);
}
}
}
private String escapeJson(final String text) {
return text.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\b", "\\b")
.replace("\f", "\\f")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}


}
}
72 changes: 63 additions & 9 deletions src/test/java/com/techatpark/sjson/JsonSchemaTest.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,76 @@
package com.techatpark.sjson;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class JsonSchemaTest {
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class JsonSchemaTest {

private JsonSchema jsonSchema;

@BeforeEach
public void setUp() {
jsonSchema = new JsonSchema();
}

@Test
public void testJsonTextWithSimpleMap() {
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("key1", "value1");
jsonMap.put("key2", 123);

String expectedJson = "{\"key1\":\"value1\",\"key2\":123}";
assertEquals(expectedJson, jsonSchema.jsonText(jsonMap));
}

@Test
void read() throws IOException {
public void testJsonTextWithNestedMap() {
Map<String, Object> nestedMap = new HashMap<>();
nestedMap.put("nestedKey1", "nestedValue1");

JsonSchema jsonSchema =
new JsonSchema(new FileReader("src/test/resources/schemas/product.json"));
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("key1", "value1");
jsonMap.put("key2", nestedMap);

jsonSchema.read(new FileReader("src/test/resources/schemas/person.json"));
String expectedJson = "{\"key1\":\"value1\",\"key2\":{\"nestedKey1\":\"nestedValue1\"}}";
assertEquals(expectedJson, jsonSchema.jsonText(jsonMap));
}

jsonSchema.jsonText(new HashMap<>());
@Test
public void testJsonTextWithList() {
List<Object> list = Arrays.asList("item1", 123, true);

Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("key1", list);

String expectedJson = "{\"key1\":[\"item1\",123,true]}";
assertEquals(expectedJson, jsonSchema.jsonText(jsonMap));
}

@Test
public void testJsonTextWithSpecialCharacters() {
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("key1", "value \"1\"");
jsonMap.put("key2", "line\nbreak");

String expectedJson = "{\"key1\":\"value \\\"1\\\"\",\"key2\":\"line\\nbreak\"}";
assertEquals(expectedJson, jsonSchema.jsonText(jsonMap));
}

@Test
public void testReadMethodNotImplemented() {
Exception exception = assertThrows(UnsupportedOperationException.class, () -> {
jsonSchema.read(null);
});

String expectedMessage = "Not yet implemented";
assertEquals(expectedMessage, exception.getMessage());
}
}
}
44 changes: 0 additions & 44 deletions src/test/resources/schemas/person.json

This file was deleted.

10 changes: 10 additions & 0 deletions src/test/resources/schemas/sample-product.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"firstName": "John",
"lastName": "Doe",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Anytown",
"zipcode": "12345"
}
}