All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hl7.fhir.utilities.json.parser.JsonParser Maven / Gradle / Ivy

Go to download

Builds the hapi fhir utilities. Requires the hapi-fhir-base be built first and be excluded from any other poms requiring it.

The newest version!
package org.hl7.fhir.utilities.json.parser;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

import org.hl7.fhir.utilities.SimpleHTTPClient;
import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.JsonException;
import org.hl7.fhir.utilities.json.model.JsonArray;
import org.hl7.fhir.utilities.json.model.JsonBoolean;
import org.hl7.fhir.utilities.json.model.JsonComment;
import org.hl7.fhir.utilities.json.model.JsonElement;
import org.hl7.fhir.utilities.json.model.JsonElementType;
import org.hl7.fhir.utilities.json.model.JsonLocationData;
import org.hl7.fhir.utilities.json.model.JsonNull;
import org.hl7.fhir.utilities.json.model.JsonNumber;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.json.model.JsonPrimitive;
import org.hl7.fhir.utilities.json.model.JsonProperty;
import org.hl7.fhir.utilities.json.model.JsonString;
import org.hl7.fhir.utilities.json.parser.JsonLexer.State;
import org.hl7.fhir.utilities.json.parser.JsonLexer.TokenType;

/**
 * Simple parser for JSON. This parser is not particularly quick (though it's not slow)
 * The focus for this parser is to faithfully record the line/col number of json elements
 * so that the FHIR validator can report issues by line number 
 * 
 * Also, for the validator, the parser will accept duplicate property names 
 * 
 * JSON5: When running in Json5 mode, the parser accepts
 *   * unquoted strings for both fields and values 
 *   * missing commas in objects and arrays 
 *   * comments - anything starting // will be processed as a comma to the end of the line
 *   
 * Other JSON5 features might be added in the future
 *   
 * The FHIR Validator uses this parser in Json5 mode, and the object model is marked up 
 * with deviations from base JSON spec so that the validator can record them as errors 
 * (this is better than blowing up parsing the JSON)
 *   
 * @author grahamegrieve
 *
 */
public class JsonParser {

  public static JsonObject parseObject(InputStream stream) throws IOException, JsonException {
    return new JsonParser().parseJsonObject(TextFile.streamToString(stream), false, false);
  }
  
  public static JsonObject parseObject(byte[] content) throws IOException, JsonException {
    return new JsonParser().parseJsonObject(TextFile.bytesToString(content), false, false);
  }

  public static JsonObject parseObject(String source) throws IOException, JsonException {
    return new JsonParser().parseJsonObject(source, false, false);
  }
  
  public static JsonObject parseObject(File source) throws IOException, JsonException {
    if (!source.exists()) {
      throw new IOException("File "+source+" not found");
    }
    return new JsonParser().setSourceName(source.getAbsolutePath()).parseJsonObject(TextFile.fileToString(source), false, false);
  }
  
  public static JsonObject parseObjectFromFile(String source) throws IOException, JsonException {
    return new JsonParser().setSourceName(source).parseJsonObject(TextFile.fileToString(source), false, false);
  }
  
  public static JsonObject parseObjectFromUrl(String source) throws IOException, JsonException {
    return new JsonParser().setSourceName(source).parseJsonObject(TextFile.bytesToString(fetch(source)), false, false);
  }
  
  public static JsonObject parseObject(InputStream stream, boolean isJson5) throws IOException, JsonException {
    return new JsonParser().parseJsonObject(TextFile.streamToString(stream), isJson5, false);
  }
  
  public static JsonObject parseObject(byte[] content, boolean isJson5) throws IOException, JsonException {
    return new JsonParser().parseJsonObject(TextFile.bytesToString(content), isJson5, false);
  }
    
  public static JsonObject parseObject(String source, boolean isJson5) throws IOException, JsonException {
    return new JsonParser().parseJsonObject(source, isJson5, false);
  }
  
  public static JsonObject parseObjectFromUrl(String source, boolean isJson5) throws IOException, JsonException {
    return new JsonParser().setSourceName(source).parseJsonObject(TextFile.bytesToString(fetch(source)), isJson5, false);
  }
  
  public static JsonObject parseObject(InputStream stream, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
    return parseObject(TextFile.streamToString(stream), isJson5, allowDuplicates);
  }
  
  public static JsonObject parseObject(byte[] stream, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
    return parseObject(TextFile.bytesToString(stream), isJson5, allowDuplicates);
  }

  public static JsonObject parseObject(String source, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
    return new JsonParser().parseJsonObject(source, isJson5, allowDuplicates);
  }
  
  // ================================================================
  
  public static JsonElement parse(InputStream stream) throws IOException, JsonException {
    return parse(TextFile.streamToString(stream));
  }
  
  public static JsonElement parse(byte[] stream) throws IOException, JsonException {
    return parse(TextFile.bytesToString(stream));
  }

  public static JsonElement parse(String source) throws IOException, JsonException {
    return parse(source, false);
  }
  
  public static JsonElement parse(File source) throws IOException, JsonException {
    return parse(TextFile.fileToString(source));
  }
  
  public static JsonElement parseFromFile(String source) throws IOException, JsonException {
    return parse(TextFile.fileToString(source));
  }
  
  public static JsonElement parseFromUrl(String source) throws IOException, JsonException {
    return parse(fetch(source));
  }
  
  public static JsonElement parse(InputStream stream, boolean isJson5) throws IOException, JsonException {
    return parse(TextFile.streamToString(stream), isJson5);
  }
  
  public static JsonElement parse(byte[] stream, boolean isJson5) throws IOException, JsonException {
    return parse(TextFile.bytesToString(stream), isJson5);
  }
    
  public static JsonElement parse(String source, boolean isJson5) throws IOException, JsonException {
    return parse(source, isJson5, false);
  }
  
  public static JsonElement parseFromUrl(String source, boolean isJson5) throws IOException, JsonException {
    return parse(fetch(source), isJson5);
  }
  
  public static JsonElement parse(InputStream stream, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
    return parse(TextFile.streamToString(stream), isJson5, allowDuplicates);
  }
  
  public static JsonElement parse(byte[] stream, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
    return parse(TextFile.bytesToString(stream), isJson5, allowDuplicates);
  }
    
  public static JsonElement parse(String source, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
    return new JsonParser().parseJsonElement(source, isJson5, allowDuplicates);
  }
  

  // ================================================================
  
  public static String compose(JsonElement element) {
    return compose(element, false);
  }
  
  public static void compose(JsonElement element, OutputStream stream) throws IOException {
    compose(element, stream, false);    
  }
  
  public static void compose(JsonElement element, File file) throws IOException {
    FileOutputStream fo = new FileOutputStream(file);
    compose(element, fo, false);
    fo.close();
  }
  
  public static byte[] composeBytes(JsonElement element) {
    return composeBytes(element, false);    
  }

  public static String compose(JsonElement element, boolean pretty) {
    return new JsonParser().write(element, pretty);
  }

  public static void compose(JsonElement element, OutputStream stream, boolean pretty) throws IOException {
    byte[] cnt = composeBytes(element, pretty);
    stream.write(cnt);
  }
  public static void compose(JsonElement element, File file, boolean pretty) throws IOException {
    byte[] cnt = composeBytes(element, pretty);
    FileOutputStream fo = new FileOutputStream(file);
    fo.write(cnt);
    fo.close();
  }

  public static byte[] composeBytes(JsonElement element, boolean pretty) {
    String s = compose(element, pretty); 
    return s.getBytes(StandardCharsets.UTF_8);
  }
  
  // ================================================================

  enum ItemType {
    Object, String, Number, Boolean, Array, End, Eof, Null;
  }
  private JsonLexer lexer;
  private ItemType itemType = ItemType.Object;
  private String itemName;
  private String itemValue;
  private boolean allowDuplicates = true;
  private boolean allowComments = false;
  private boolean allowNoComma = false;
  private JsonLocationData startProperty;
  private JsonLocationData endProperty;
  private boolean itemNoComma;
  private boolean allowUnquotedStrings;
  private boolean itemUnquoted;
  private boolean valueUnquoted;
  private String sourceName;

  private JsonObject parseJsonObject(String source, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
    this.allowDuplicates = allowDuplicates;
    this.allowComments = isJson5;
    this.allowNoComma = isJson5;
    this.allowUnquotedStrings = isJson5;
    return parseSource(Utilities.stripBOM(source));
  }

  private JsonObject parseSource(String source) throws IOException, JsonException {
    lexer = new JsonLexer(source, allowComments, allowUnquotedStrings);
    lexer.setSourceName(sourceName);
    JsonObject result = new JsonObject();
    lexer.takeComments(result);
    result.setStart(lexer.getLastLocationAWS().copy());
    if (lexer.getType() == TokenType.Open) {
      lexer.next();
      lexer.getStates().push(new State("", true));
    } 
    else if (lexer.getType() != null) {
      throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString());
    } else {
      throw lexer.error("Unexpected content at start of JSON");      
    }

    if (lexer.getType() != TokenType.Close) {
      parseProperty();
      readObject("$", result, true);
    }
    result.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
    return result;
  }
  
  private JsonElement parseJsonElement(String source, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
    this.allowDuplicates = allowDuplicates;
    this.allowComments = isJson5;
    this.allowNoComma = isJson5;
    this.allowUnquotedStrings = isJson5;
    return parseSourceElement(Utilities.stripBOM(source));
  }
  
  private JsonElement parseSourceElement(String source) throws IOException, JsonException {
    lexer = new JsonLexer(source, allowComments, allowUnquotedStrings);
    switch (lexer.getType()) {
    case Boolean:
      JsonBoolean bool = new JsonBoolean(lexer.getValue().equals("true"));
      lexer.takeComments(bool);
      bool.setStart(lexer.getLastLocationAWS().copy());
      bool.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
      return bool;   
    case Null:
      JsonNull nll = new JsonNull();
      lexer.takeComments(nll);
      nll.setStart(lexer.getLastLocationAWS().copy());
      nll.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
      return nll;   
    case Number:
      JsonNumber num = new JsonNumber(lexer.getValue());
      lexer.takeComments(num);
      num.setStart(lexer.getLastLocationAWS().copy());
      num.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
      return num;   
    case Open:
      JsonObject obj = new JsonObject();
      lexer.takeComments(obj);
      obj.setStart(lexer.getLastLocationAWS().copy());
      if (lexer.getType() == TokenType.Open) {
        lexer.next();
        lexer.getStates().push(new State("", true));
      } 
      else
        throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString());

      if (lexer.getType() != TokenType.Close) {
        parseProperty();
        readObject("$", obj, true);
      }
      obj.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
      return obj;   
    case OpenArray:
      JsonArray arr = new JsonArray();
      lexer.takeComments(arr);
      arr.setStart(lexer.getLastLocationAWS().copy());
      lexer.next();
      lexer.getStates().push(new State("", false));
      if (lexer.getType() != TokenType.CloseArray) {
        parseProperty();
        readArray("$", arr, true);
      }
      arr.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
      return arr; 
    case String:
      JsonString str = new JsonString(lexer.getValue());
      lexer.takeComments(str);
      str.setStart(lexer.getLastLocationAWS().copy());
      str.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
      return str;   
    default:
    }
    throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString());    
  }

  private void readObject(String path, JsonObject obj, boolean root) throws IOException, JsonException {
    while (!(itemType == ItemType.End) || (root && (itemType == ItemType.Eof))) {
      obj.setExtraComma(false);
      switch (itemType) {
      case Object:
        JsonObject child = new JsonObject(); //(obj.path+'.'+ItemName);
        child.setStart(startProperty.copy());
        lexer.takeComments(child);
        if (obj.has(itemName) && !allowDuplicates)
          throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
        obj.addForParser(itemName, child, itemNoComma, itemUnquoted, valueUnquoted);
        next();
        readObject(path+"."+itemName, child, false);
        child.setEnd(endProperty.copy());
        break;
      case Boolean :
        JsonBoolean childB = new JsonBoolean(Boolean.valueOf(itemValue));
        childB.setStart(startProperty.copy());
        lexer.takeComments(childB);
        if (obj.has(itemName) && !allowDuplicates)
          throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
        obj.addForParser(itemName, childB, itemNoComma, itemUnquoted, valueUnquoted);
        childB.setEnd(endProperty.copy());
        break;
      case String:
        JsonString childS = new JsonString(itemValue);
        childS.setStart(startProperty.copy());
        lexer.takeComments(childS);
        if (obj.has(itemName) && !allowDuplicates)
          throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
        obj.addForParser(itemName, childS, itemNoComma, itemUnquoted, valueUnquoted);
        childS.setEnd(endProperty.copy());
        break;
      case Number:
        JsonNumber childN = new JsonNumber(itemValue);
        childN.setStart(startProperty.copy());
        lexer.takeComments(childN);
        if (obj.has(itemName) && !allowDuplicates)
          throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
        obj.addForParser(itemName, childN, itemNoComma, itemUnquoted, valueUnquoted);
        childN.setEnd(endProperty.copy());
        break;
      case Null:
        JsonNull childn = new JsonNull();
        childn.setStart(startProperty.copy());
        lexer.takeComments(childn);
        if (obj.has(itemName) && !allowDuplicates)
          throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
        obj.addForParser(itemName, childn, itemNoComma, itemUnquoted, valueUnquoted);
        childn.setEnd(endProperty.copy());
        break;
      case Array:
        JsonArray childA = new JsonArray(); // (obj.path+'.'+ItemName);
        childA.setStart(startProperty.copy());
        lexer.takeComments(childA);
        if (obj.has(itemName) && !allowDuplicates)
          throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
        obj.addForParser(itemName, childA, itemNoComma, itemUnquoted, valueUnquoted);
        next();
        if (!readArray(path+"."+itemName, childA, false))
          next(true);
        if (childA.getEnd() == null) {
          childA.setEnd(endProperty.copy());
        }
        break;
      case Eof : 
        throw lexer.error("Unexpected End of File");
      case End:
        throw lexer.error("Unexpected End"); // Don't think we can get here
      }
      itemNoComma = false;
      endProperty = lexer.getLocation().copy();
      obj.setExtraComma(lexer.getType() == TokenType.Comma);
      next();
    }
  }

  private boolean readArray(String path, JsonArray arr, boolean root) throws IOException, JsonException {
    boolean res = false;
    while (!((itemType == ItemType.End) || (root && (itemType == ItemType.Eof)))) {
      res  = true;
      arr.setExtraComma(false);
      switch (itemType) {
      case Object:
        JsonObject obj  = new JsonObject(); // (arr.path+'['+inttostr(i)+']');
        obj.setStart(startProperty.copy());
        lexer.takeComments(obj);
        arr.addForParser(obj, itemNoComma, valueUnquoted);
        next();
        readObject(path+"["+(arr.size()-1)+"]", obj, false);
        obj.setEnd(endProperty.copy());
        break;
      case String:
        JsonString s = new JsonString(itemValue);
        s.setStart(startProperty.copy());
        lexer.takeComments(s);
        arr.addForParser(s, itemNoComma, valueUnquoted);
        s.setEnd(endProperty.copy());
        break;
      case Number:
        JsonNumber n = new JsonNumber(itemValue);
        n.setStart(startProperty.copy());
        lexer.takeComments(n);
        arr.addForParser(n, itemNoComma, valueUnquoted);
        n.setEnd(endProperty.copy());
        break;
      case Boolean:
        JsonBoolean b = new JsonBoolean("true".equals(itemValue));
        b.setStart(startProperty.copy());
        lexer.takeComments(b);
        arr.addForParser(b, itemNoComma, valueUnquoted);
        b.setEnd(endProperty.copy());
        break;
      case Null :
        JsonNull nn = new JsonNull();
        nn.setStart(startProperty.copy());
        lexer.takeComments(nn);
        arr.addForParser(nn, itemNoComma, valueUnquoted);
        nn.setEnd(endProperty.copy());
        break;
      case Array:
        JsonArray child = new JsonArray(); // (arr.path+'['+inttostr(i)+']');
        child.setStart(startProperty.copy());
        lexer.takeComments(child);
        arr.addForParser(child, itemNoComma, valueUnquoted);
        next();
        readArray(path+"["+(arr.size()-1)+"]", child, false);
        child.setEnd(endProperty.copy());
        break;
      case Eof : 
        throw lexer.error("Unexpected End of File");
      case End:
        throw lexer.error("Can't get here");
      }
      itemNoComma = false;
      arr.setEnd(lexer.getLocation().copy());
      arr.setExtraComma(lexer.getType() == TokenType.Comma);
      next();
    }
    return res;
  }

  private void next() throws IOException {
    next(false);
  }
  
  private void next(boolean noPop) throws IOException {
    switch (itemType) {
    case Object :
      lexer.consume(TokenType.Open);
      lexer.getStates().push(new State(itemName, true));
      if (lexer.getType() == TokenType.Close) {
        itemType = ItemType.End;
        lexer.next();
      } else
        parseProperty();
      break;
    case Null:
    case String:
    case Number: 
    case End: 
    case Boolean :
      if (itemType == ItemType.End && !noPop)
        lexer.getStates().pop();
      if (lexer.getType() == TokenType.Comma) {
        lexer.next();
        if (allowNoComma && (lexer.getType() == TokenType.CloseArray || lexer.getType() == TokenType.Close)) {
          itemType = ItemType.End;
          lexer.next();
        } else {
          parseProperty();
        }
      } else if (lexer.getType() == TokenType.Close) {
        itemType = ItemType.End;
        lexer.next();
      } else if (lexer.getType() == TokenType.CloseArray) {
        itemType = ItemType.End;
        lexer.next();
      } else if (lexer.getType() == TokenType.Eof) {
        itemType = ItemType.Eof;
      } else if (allowNoComma && (lexer.getType() == TokenType.String || (!lexer.getStates().peek().isProp()) && lexer.getType().isValueType())) {
        itemNoComma = true;
        parseProperty();        
      } else {
        throw lexer.error("Unexpected JSON syntax");
      }
      break;
    case Array :
      lexer.next();
      lexer.getStates().push(new State(itemName+"[]", false));
      parseProperty();
      break;
    case Eof :
      throw lexer.error("JSON Syntax Error - attempt to read past end of json stream");
    default:
      throw lexer.error("not done yet (a): "+itemType.toString());
    }
  }

  private void parseProperty() throws IOException {
    if (lexer.getStates().peek().isProp()) {
      itemUnquoted = lexer.isUnquoted();
      itemName = lexer.consume(TokenType.String);
      itemValue = null;
      lexer.consume(TokenType.Colon);
    }
    startProperty = lexer.getLastLocationAWS().copy();
    endProperty = lexer.getLocation().copy();
    valueUnquoted = lexer.isUnquoted();
    switch (lexer.getType()) {
    case Null :
      itemType = ItemType.Null;
      itemValue = lexer.getValue();
      lexer.next();
      break;
    case String :
      itemType = ItemType.String;
      itemValue = lexer.getValue();
      lexer.next();
      break;
    case Boolean :
      itemType = ItemType.Boolean;
      itemValue = lexer.getValue();
      lexer.next();
      break;
    case Number :
      itemType = ItemType.Number;
      itemValue = lexer.getValue();
      lexer.next();
      break;
    case Open :
      itemType = ItemType.Object;
      break;
    case OpenArray :
      itemType = ItemType.Array;
      break;
    case CloseArray :
      itemType = ItemType.End;
      break;
      // case Close, , case Colon, case Comma, case OpenArray,       !
    default:
      throw lexer.error("not done yet (b): "+lexer.getType().toString());
    }
  }

  private String write(JsonElement element, boolean pretty) {
    StringBuilder b = new StringBuilder();
    if (pretty && element.hasComments()) {
      writeComments(b, element.getComments(), 0);
    }
    write(b, element, pretty, 0);
    if (pretty) {
      b.append("\n");
    }
    return b.toString();
  }

  private void writeComments(StringBuilder b, List comments, int indent) {
    for (JsonComment s : comments) {
      b.append("// ");
      b.append(s.getContent());
      b.append("\n");
      b.append(Utilities.padLeft("", ' ', indent));
    }
  }

  private void write(StringBuilder b, JsonElement e, boolean pretty, int indent) {
    switch (e.type()) {
    case ARRAY:
      JsonArray arr = (JsonArray) e;
      b.append("[");
      boolean first = true;
      boolean complex = arr.size() > 6; // arbitrary cut off
      if (!complex) {
        int length = 0;
        for (JsonElement i : arr.getItems()) {
          if (i instanceof JsonPrimitive) {
            length = length + ((JsonPrimitive)i).toJson().length();
          }
          if (i.type() == JsonElementType.ARRAY || i.type() == JsonElementType.OBJECT
              || i.hasComments()) { // 20 is a somewhat arbitrary cut off
            complex = true;
          }
        }
        if (length > 60) {
          complex = true;
        }
      }
      for (JsonElement i : arr.getItems()) {
        if (first) first = false; else b.append(pretty && !complex ? ", " : ",");
        if (pretty && complex) {
          b.append("\n");
          b.append(Utilities.padLeft("", ' ', indent+2));
          if (i.hasComments()) {
            writeComments(b, i.getComments(), indent+2);
          }
        }
        write(b, i, pretty && complex, indent+2);
      }
      if (pretty && complex) {
        b.append("\n");
        b.append(Utilities.padLeft("", ' ', indent));
      }
      b.append("]");
      break;
    case BOOLEAN:
      b.append(((JsonBoolean) e).getValue());
      break;
    case NULL:
      b.append(((JsonNull) e).getValue());
      break;
    case NUMBER:
      b.append(((JsonNumber) e).getValue());
      break;
    case OBJECT:
      b.append("{");
      first = true;
      for (JsonProperty p : ((JsonObject) e).getProperties()) {
        if (first) first = false; else b.append(",");
        if (pretty) {
          b.append("\n");
          b.append(Utilities.padLeft("", ' ', indent+2));
          if (p.getValue().hasComments()) {
            writeComments(b, p.getValue().getComments(), indent+2);
          }
        }
        b.append("\"");
        b.append(p.getName());
        b.append(pretty ? "\" : " : "\":");
        write(b, p.getValue(), pretty, indent+2);
      }
      if (pretty) {
        b.append("\n");
        b.append(Utilities.padLeft("", ' ', indent));
      }
      b.append("}");
      break;
    case STRING:
      b.append("\"");
      b.append(Utilities.escapeJson(((JsonString) e).getValue()));
      b.append("\"");
      break;
    default:
      throw new Error("Can't get here");    
    }
  }

  private static byte[] fetch(String source) throws IOException {
    SimpleHTTPClient fetcher = new SimpleHTTPClient();
    fetcher.addHeader("Accept", "application/json, application/fhir+json");
    String murl = source.contains("?") ? source+"&nocache=" + System.currentTimeMillis() : source+"?nocache=" + System.currentTimeMillis();
    HTTPResult res = fetcher.get(murl);
    res.checkThrowException();
    return res.getContent();
  }

  public String getSourceName() {
    return sourceName;
  }

  public JsonParser setSourceName(String sourceName) {
    this.sourceName = sourceName;
    return this;
  }
  
  
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy