
com.sta.cts.JSONScanner Maven / Gradle / Ivy
package com.sta.cts;
import java.io.IOException;
import java.io.StringReader;
import java.util.Date;
import java.util.Hashtable;
import com.sta.mlogger.MLogger;
/**
* Name: JSONScanner
* Description: .
*
* Comment: ...
*
* Copyright: Copyright (c) 2018, 2019, 2021
* Company: >StA-Soft<
* @author StA
* @version 1.0
*/
public class JSONScanner extends Scanner
{
// private static final String NULL_MAGIC = "___NULL_MAGIC___";
/**
* Im JSON-Scanner verwendete Symbole.
*/
public enum JSONSym
{
/**
* Array-Anfang ("[").
*/
ARRAY_BEGIN,
/**
* Array-Ende ("]").
*/
ARRAY_END,
/**
* Object-Anfang ("{").
*/
OBJECT_BEGIN,
/**
* Object-Ende ("}").
*/
OBJECT_END,
/**
* Doppelpunkt (":").
*/
COLON,
/**
* Komma (",").
*/
COMMA,
/**
* Null ("null").
*/
NULL
}
/**
* Operations-Level f?r JSON-Bl?tter.
*/
protected int myLevel = 0;
/**
* Aktuell zu verwendende JSON-Blatt-Hash-Tabelle.
*/
protected LeafHashtable myLHT = null;
//===========================================================================
/**
* Standard-Constructor.
*/
public JSONScanner()
{
}
//===========================================================================
@Override
public void initH(Hashtable pHT)
{
// preOverread(); // siehe: init(is, enc)
}
//---------------------------------------------------------------------------
/**
* ?berlie?t Spaces incl. #$09, #$0d, #$0a. Bei anderen Zeichen erfolgt ein
* Abbruch. Das entscheidende Zeichen wird gelesen und zur?ckgeliefert.
* @return letztes und entscheidendes Zeichen
* @throws IOException falls ein Lesefehler auftritt
*/
protected char overreadSpaces() throws IOException
{
char ch;
do
{
ch = getChar();
}
while ((ch == ' ') || (ch == 0x09) || (ch == 0x0d) || (ch == 0x0a));
return ch;
}
/**
* Zeichenkette parsen. Trennzeichen am Anfang muss bereits gelesen sein.
* Abschlie?endes Trennzeichen wird ?berlesen.
* @return Text f?r Zeichenkette (Inhalt zwischen Trennzeichen)
* @throws IOException im Fehlerfall
*/
private String scanString() throws IOException
{
StringBuilder sb = new StringBuilder();
boolean ex = false;
while (!ex)
{
char ch = getChar();
if (ch == 0x00)
{
MLogger.wrn("Missing end of String.");
ex = true;
}
else if (ch == '\"')
{
// ungetChar(ch);
ex = true;
}
else if (ch == '\\')
{
char ch1 = getChar();
if (ch1 == '\\')
{
sb.append(ch1);
}
else if (ch1 == '\"')
{
sb.append(ch1);
}
else if (ch1 == '/')
{
sb.append(ch1);
}
else if (ch1 == 'b')
{
sb.append('\b');
}
else if (ch1 == 'f')
{
sb.append('\f');
}
else if (ch1 == 'n')
{
sb.append('\n');
}
else if (ch1 == 'r')
{
sb.append('\r');
}
else if (ch1 == 't')
{
sb.append('\t');
}
else if (ch1 == 'u')
{
StringBuilder sb1 = new StringBuilder();
for (int i = 0; i < 4; i++)
{
ch1 = getChar();
if (((ch1 >= '0') && (ch1 <= '9')) || ((ch1 >= 'a') && (ch1 <= 'f')) || ((ch1 >= 'A') && (ch1 <= 'F')))
{
sb1.append(ch1);
}
else
{
throw new IOException("Invalid \\u combination: " + sb.toString() + ch1);
}
}
int ic = Integer.valueOf(sb1.toString(), 16);
sb.append((char) ic);
}
else
{
MLogger.wrn("Invalid character in mask sequence: " + ch1);
ungetChar(ch1);
ex = true;
}
}
else
{
sb.append(ch);
}
}
return sb.toString();
}
/**
* Schl?sselwort scannen.
* @return Schl?sselwort
* @throws IOException im Fehlerfall
*/
private String scanKeyWord() throws IOException
{
StringBuilder sb = new StringBuilder();
boolean ex = false;
while (!ex)
{
char ch = getChar();
if (ch == 0x00)
{
ex = true;
}
else if ((ch >= 'a') && (ch <= 'z'))
{
sb.append(ch);
}
else
{
ungetChar(ch);
ex = true;
}
}
return sb.toString();
}
/**
* Zahl (Integer/Long und/oder Float/Double) scannen.
* @return Number
* @throws IOException im Fehlerfall
*/
private Number scanNumber() throws IOException
{
StringBuilder sb = new StringBuilder();
boolean isIntLong = true;
boolean ex = false;
while (!ex)
{
char ch = getChar();
if (ch == 0x00)
{
ex = true;
}
else if ((ch >= '0') && (ch <= '9'))
{
sb.append(ch);
}
else if ((ch == '+') || (ch == '-'))
{
sb.append(ch);
}
else if ((ch == '.') || (ch == 'E'))
{
sb.append(ch);
isIntLong = false;
}
else
{
ungetChar(ch);
ex = true;
}
}
String s = sb.toString();
if (s.length() > 0)
{
if (isIntLong)
{
Long v = UniTypeConv.convString2Long(s);
return ((v <= Integer.MAX_VALUE) && (v >= Integer.MIN_VALUE)) ? v.intValue() : v;
}
else
{
return UniTypeConv.convString2Double(s);
}
}
return null;
}
@Override
public Object getNewToken()
{
Object token = null;
try
{
boolean ex = false;
while (!ex)
{
char ch = overreadSpaces();
if (ch == 0x00)
{
ex = true;
}
else if (ch == '[')
{
token = JSONSym.ARRAY_BEGIN;
ex = true;
}
else if (ch == ']')
{
token = JSONSym.ARRAY_END;
ex = true;
}
else if (ch == '{')
{
token = JSONSym.OBJECT_BEGIN;
ex = true;
}
else if (ch == '}')
{
token = JSONSym.OBJECT_END;
ex = true;
}
else if (ch == ':')
{
token = JSONSym.COLON;
ex = true;
}
else if (ch == ',')
{
token = JSONSym.COMMA;
ex = true;
}
else if (ch == '\"')
{
token = scanString();
ex = true;
}
else if ((ch >= '0') && (ch <= '9'))
{
ungetChar(ch);
token = scanNumber();
ex = true;
}
else if ((ch >= 'a') && (ch <= 'z'))
{
ungetChar(ch);
String s = scanKeyWord();
if ("true".equals(s))
{
return true;
}
else if ("false".equals(s))
{
return false;
}
else if ("null".equals(s))
{
return JSONSym.NULL;
}
else
{
MLogger.wrn("Invalid key word: " + s);
return s;
}
}
else
{
MLogger.wrn("Unknown character: " + ch);
}
}
}
catch (IOException e)
{
}
return token;
}
/**
* Pr?fen, ob ein bestimmtes Symbol folgt, und falls ja: ?berlesen.
* @param sym Symbol
* @return true: ja, Symbol gefunden, Symbol wurde ?berlesen, false: nein, anderes Symbol, Symbol wurde nicht ?berlesen
*/
public boolean check(JSONSym sym)
{
Object token = getToken();
if (token != sym)
{
ungetToken(token);
return false;
}
return true;
}
/**
* Es wird gepr?ft, ob ein Blatt mit dem angegebenen Namen folgt. Falls ja, werden Name, ":" und Wert gelesen, ein optional
* folgendes "," ?berlesen und der Wert zur?ckgegeben. Falls nein, wird der Name zur?ckgelegt und null zur?ckgeliefert.
* @param pName Name/Schl?ssel des Blatts
* @return Wert des Blatts, falls vorhanden, sonst null
* @throws Exception im Fehlerfall
*/
public Object getLeaf(String pName) throws Exception
{
Object token = getToken();
if ((token instanceof String) && (token.equals(pName)))
{
token = getToken();
if (token == JSONSym.COLON)
{
token = getToken();
if (token == null)
{
return null;
}
else if (token instanceof String)
{
check(JSONSym.COMMA);
return token;
}
else if (token instanceof Number)
{
check(JSONSym.COMMA);
return token;
}
else if (token instanceof Boolean)
{
check(JSONSym.COMMA);
return token;
}
else if (token == JSONSym.NULL)
{
check(JSONSym.COMMA);
return null;
}
else
{
MLogger.wrn("Invalid value: " + token);
}
}
else
{
ungetToken(token);
throw new Exception("Missing \":\", found: " + token);
}
}
else
{
ungetToken(token);
}
return null;
}
/**
* String-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als String
* @throws Exception siehe getLeaf
*/
public String getLeafString(String pName) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof String)
{
return (String) obj;
}
throw new Exception("Invalid leaf type. Expected: String, found: " + obj);
}
/**
* Integer-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als Integer
* @throws Exception siehe getLeaf
*/
public Integer getLeafInt(String pName) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof Integer)
{
return (Integer) obj;
}
if (obj instanceof Long)
{
return ((Long) obj).intValue();
}
throw new Exception("Invalid leaf type. Expected: Integer, found: " + obj);
}
/**
* Long-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als Long
* @throws Exception siehe getLeaf
*/
public Long getLeafLong(String pName) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof Long)
{
return (Long) obj;
}
if (obj instanceof Integer)
{
return ((Integer) obj).longValue();
}
throw new Exception("Invalid leaf type. Expected: Long, found: " + obj);
}
/**
* Float-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als Float
* @throws Exception siehe getLeaf
*/
public Float getLeafFloat(String pName) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof Float)
{
return (Float) obj;
}
if (obj instanceof Double)
{
return ((Double) obj).floatValue();
}
if (obj instanceof Integer)
{
return ((Integer) obj).floatValue();
}
if (obj instanceof Long)
{
return ((Long) obj).floatValue();
}
throw new Exception("Invalid leaf type. Expected: Float, found: " + obj);
}
/**
* Double-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als Double
* @throws Exception siehe getLeaf
*/
public Double getLeafDouble(String pName) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof Double)
{
return (Double) obj;
}
if (obj instanceof Float)
{
return ((Float) obj).doubleValue();
}
if (obj instanceof Integer)
{
return ((Integer) obj).doubleValue();
}
if (obj instanceof Long)
{
return ((Long) obj).doubleValue();
}
throw new Exception("Invalid leaf type. Expected: Double, found: " + obj);
}
/**
* Date-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @param pMask optionale Maske
* @return Inhalt als Date (nur Datum)
* @throws Exception siehe getLeaf
*/
public Date getLeafDate(String pName, String pMask) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof String)
{
return UniTypeConv.convString2Date((String) obj, pMask);
}
throw new Exception("Invalid leaf type. Expected: String for Date, found: " + obj);
}
/**
* Date-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als Date (nur Datum)
* @throws Exception siehe getLeaf
*/
public Date getLeafDate(String pName) throws Exception
{
return getLeafDate(pName, null);
}
/**
* Time-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @param pMask optionale Maske
* @return Inhalt als Date (nur Zeitangabe)
* @throws Exception siehe getLeaf
*/
public Date getLeafTime(String pName, String pMask) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof String)
{
return UniTypeConv.convString2Time((String) obj, pMask);
}
throw new Exception("Invalid leaf type. Expected: String for Time, found: " + obj);
}
/**
* Time-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als Date (nur Zeitangabe)
* @throws Exception siehe getLeaf
*/
public Date getLeafTime(String pName) throws Exception
{
return getLeafTime(pName, null);
}
/**
* DateTime-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @param pMask optionale Maske
* @return Inhalt als Date (Datum und Zeitangabe)
* @throws Exception siehe getLeaf
*/
public Date getLeafDateTime(String pName, String pMask) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof String)
{
return UniTypeConv.convString2DateTime((String) obj, pMask);
}
throw new Exception("Invalid leaf type. Expected: String for DateTime, found: " + obj);
}
/**
* DateTime-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als Date (Datum und Zeitangabe)
* @throws Exception siehe getLeaf
*/
public Date getLeafDateTime(String pName) throws Exception
{
return getLeafDateTime(pName, null);
}
/**
* Boolean-JSON-Blatt lesen.
* @param pName der Name des JSON-Blatt-Tags
* @return Inhalt als Boolean
* @throws Exception siehe getLeaf
*/
public Boolean getLeafBool(String pName) throws Exception
{
Object obj = getLeaf(pName);
if (obj == null)
{
return null;
}
if (obj instanceof Boolean)
{
return (Boolean) obj;
}
throw new Exception("Invalid leaf type. Expected: Boolean, found: " + obj);
}
/**
* myLevel erh?hen.
*/
public void incLevel()
{
myLevel++;
}
/**
* myLevel verringern.
*/
public void decLevel()
{
myLevel--;
}
/**
* Ermittlung der LeafHashtable, um z. B. zu pr?fen, ob diese nach Laden
* eines TO's auch wirklich leer ist.
* @return die LHT
*/
public LeafHashtable getLHT()
{
return myLHT;
}
/**
* Spezielle Hashtabelle aus JSON-Bl?ttern erzeugen.
* In der Hashtabelle liegen unter den JSON-Blatt(-Tag)-Namen (String) als
* Schl?ssel der Wert des jeweiligen Blatts (ebenfalls als String).
* Object-Anfang muss gelesen worden sein. Object-Ende wird nicht ?berlesen.
* @return diese Hashtabelle
* @throws Exception falls ein Lesefehler auftritt, ebenso im Falle von JSON-Struktur-Fehlern
*/
public LeafHashtable getLeafs() throws Exception
{
if (myLevel != 0)
{
return myLHT;
}
myLHT = new LeafHashtable();
boolean cont = false;
do
{
Object token = getToken();
if (!(token instanceof String))
{
ungetToken(token);
// Falls nach einem Blatt bereits ein "," gelesen wurde, dann MUSS ein neues Blatt (und somit ein String) kommen!
// if (cont)
// {
// throw new Exception("JSON-Error 0 (missing name/key for leaf, found: " + (token != null ? token.toString() : "(null)") + ")");
// }
break;
}
String name = (String) token;
token = getToken();
if (token != JSONSym.COLON)
{
throw new Exception("JSON-Error 1 (missing colon ':' in leaf: " + name + ", found: " + (token != null ? token.toString() : "(null)") + ")");
}
token = getToken();
String s = "";
if (token instanceof String)
{
s = (String) token;
}
else if (token instanceof Integer)
{
s = UniTypeConv.convInt2String((Integer) token);
}
else if (token instanceof Long)
{
s = UniTypeConv.convLong2String((Long) token);
}
else if (token instanceof Float)
{
s = UniTypeConv.convFloat2String((Float) token);
}
else if (token instanceof Double)
{
s = UniTypeConv.convDouble2String((Double) token);
}
else if (token instanceof Boolean)
{
s = UniTypeConv.convBool2String((Boolean) token);
}
else
{
throw new Exception("JSON-Error 2 (invalid value type in leaf: " + name + ", found: " + (token != null ? token.toString() : "(null)") + ")");
}
myLHT.put(name, s);
cont = check(JSONSym.COMMA);
}
while (cont);
return myLHT;
}
/**
* Pr?fen, ob die Leaf-Hash-Tabelle (LHT) leer ist, falls nicht: Meldungen (als Warnungen) ausgeben.
* @param lht die LHT
* @param text Text f?r die Meldung.
*/
public static void checkLHT(LeafHashtable lht, String text)
{
if ((lht != null) && !lht.isEmpty())
{
lht.forEach((key, value) -> MLogger.wrn("LHT (" + text + ") not empty: " + "\"" + key + "\" : " + value));
/*
Enumeration e = lht.keys();
while (e.hasMoreElements())
{
String key = (String) e.nextElement();
String val = (String) lht.get(key);
MLogger.wrn("LHT (" + text + ") not empty: " + "\"" + key + "\" : " + val);
}
*/
MLogger.wrn("Please check JSON-Source-File!");
}
}
/**
* Pr?fen, ob die Leaf-Hash-Tabelle (LHT) leer ist, falls nicht: Meldungen (als Warnungen) ausgeben,
* Darf nur nach getLeafs() verwendet werden, bezieht sich auf das letzte getLeafs().
* @param text Text f?r die Meldung.
*/
public void checkLHT(String text)
{
checkLHT(myLHT, text);
}
//===========================================================================
/**
* Main-Methode.
* @param args Kommandozeilenargumente
*/
public static void main(String... args)
{
try
{
JSONScanner js = new JSONScanner();
String s = "\"Strg\" : \"Hallo!\", \"Strg1\" : \"Hallo1!\"";
js.init(new StringReader(s));
Object obj = js.getLeaf("Strg");
MLogger.deb(() -> "obj: " + obj);
Object obj1 = js.getLeaf("Strg1");
MLogger.deb(() -> "obj1: " + obj1);
js.close();
s = "\"Strg\" : \"Hallo!\", \"Strg1\" : \"Hallo1!\", \"Int\" : \"0\", \"Int1\" : \"1\"";
js.init(new StringReader(s));
LeafHashtable lht = js.getLeafs();
MLogger.deb(() -> "result: " + lht.toString());
MLogger.deb(() -> "lht-int-as-int: " + lht.getLeafInt("Int"));
MLogger.deb(() -> "lht-int-as-string: " + lht.getLeafString("Int1"));
js.checkLHT("test");
js.close();
}
catch (Exception ex)
{
MLogger.err("", ex);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy