I was working on HL7 (2.3.1) message of REF^I12 type. I'm trying to get the OBR segment from the message by using terser, but, I'm getting null whenever I try to get OBR or OBX segment values. Below is the hl7 message I'm trying to parse-
MSH|^~\&|Sample test hospital|TEST|||20191105154020||REF^I12|178038310|P|2.3.1
PID|1||179790^^^test||infection3^Test||19881128|F|||29 Mousehole Cresent^^Yanchep^WA^6035|||
PV1|1|O|29677|||||873250^Cailes^Jeremy^^^^^^test|muddu^Aung^Htun^TEST^^^^^
OBR|1|0175671960^test|0175671960^test|test123 Letter^Letter^testletter||20180725|20180719||20180725|||||||^Cailes^Jeremy^^^^^^testletter||||||||||||sunil^Shizaraj^Suma^(testpartner)^^^^^test~X0012622^Hess^Sally^(OPA)^^^^^test~I96766753^Doctor 1^Mail^^^^^^test~X20180713013100^Doctor 2^Mail^Business Name^^^^^test~FAX356^Doctor 3^Fax^(FAX)^^^^^test~||||||||20180719
OBX|1|FT|OLETTER^^test||Sample test hospital||||||F
I'm actually using terser to parse and get the value from OBR-28-1 segment. But my code is always returning null whenever I try to get the value. Below is the snippet of the code:
public void getReceipientFromOBRTest(Message messageobject) throws Exception {
String provider;
String id;
Map<String, String> map = new HashMap<>();
Terser terser = new Terser(messageObject);
for (int i = 0; i <= 10; i++) {
provider = terser.get("/.OBR-28(" + i + ")-4");
id = terser.get("/.OBR-28(" + i + ")-1");
if (provider != null && id != null) {
map.put(id, provider);
}
}
if (map.values().isEmpty()) {
System.out.println(map);
}
}
When I remove the PV1 segment from the message, I am able to get the OBR segment value. But, if PV1 segment is present, terser is unable to recognise the OBR or even OBX segment. The HL7 file seems to be valid when I parsed it through online parser.
How to get the OBR segment from the message? Is there something I'm doing wrong? Would love it if anyone can help me out here.
I was able to get the OBR segment values and below is the working code:
public void getReceipientFromOBRTest(Message messageobject) throws Exception {
String provider;
String id;
Map<String, String> map = new HashMap<>();
Terser terser = new Terser(messageObject);
for (int i = 0; i <= 10; i++) {
provider = terser.get("/.PV1PV2/OBR-28(" + i + ")-4");
id = terser.get("/.PV1PV2/OBR-28(" + i + ")-1");
if (provider != null && id != null) {
map.put(id, provider);
}
}
if (map.values().isEmpty()) {
System.out.println(map);
}
}
Related
I have written a fairly simple language in ANTLR. Before actually interpreting the code written by a user, I wish to parse the code and check for syntax errors. If found I wish to output the cause for the error and exit. How can I check the code for syntax errors and output the corresponding error. Please not that for my purposes the error statements similar to those generated by the ANTLR tool are more than sufficient. For example
line 3:0 missing ';'
There is ErrorListener that you can use to get more information.
For example:
...
FormulaParser parser = new FormulaParser(tokens);
parser.IsCompletion = options.IsForCompletion;
ErrorListener errListener = new ErrorListener();
parser.AddErrorListener(errListener);
IParseTree tree = parser.formula();
Only thing you need to do is to attach ErrorListener to the parser.
Here is the code of ErrorListener.
/// <summary>
/// Error listener recording all errors that Antlr parser raises during parsing.
/// </summary>
internal class ErrorListener : BaseErrorListener
{
private const string Eof = "the end of formula";
public ErrorListener()
{
ErrorMessages = new List<ErrorInfo>();
}
public bool ErrorOccured { get; private set; }
public List<ErrorInfo> ErrorMessages { get; private set; }
public override void SyntaxError(IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
{
ErrorOccured = true;
if (e == null || e.GetType() != typeof(NoViableAltException))
{
ErrorMessages.Add(new ErrorInfo()
{
Message = ConvertMessage(msg),
StartIndex = offendingSymbol.StartIndex,
Column = offendingSymbol.Column + 1,
Line = offendingSymbol.Line,
Length = offendingSymbol.Text.Length
});
return;
}
ErrorMessages.Add(new ErrorInfo()
{
Message = string.Format("{0}{1}", ConvertToken(offendingSymbol.Text), " unexpected"),
StartIndex = offendingSymbol.StartIndex,
Column = offendingSymbol.Column + 1,
Line = offendingSymbol.Line,
Length = offendingSymbol.Text.Length
});
}
public override void ReportAmbiguity(Antlr4.Runtime.Parser recognizer, DFA dfa, int startIndex, int stopIndex, bool exact, BitSet ambigAlts, ATNConfigSet configs)
{
ErrorOccured = true;
ErrorMessages.Add(new ErrorInfo()
{
Message = "Ambiguity", Column = startIndex, StartIndex = startIndex
});
base.ReportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs);
}
private string ConvertToken(string token)
{
return string.Equals(token, "<EOF>", StringComparison.InvariantCultureIgnoreCase)
? Eof
: token;
}
private string ConvertMessage(string message)
{
StringBuilder builder = new StringBuilder(message);
builder.Replace("<EOF>", Eof);
return builder.ToString();
}
}
It is some dummy listener, but you can see what it does. And that you can tell if the error is syntax error, or some ambiguity error. After parsing, you can ask directly the errorListener, if some error occurred.
I need to add header to a STOMP message currently it is working as below but i am recreating the message , is it possible to just add native header without having to recreate the message for performance .
public class MyChannelInterceptor extends ChannelInterceptorAdapter {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
StompCommand command = accessor.getCommand();
if(command != null) {
log.debug("Receiving msg {} from {}",command,accessor.getUser().getName());
if(command == StompCommand.SEND) {
log.debug("Adding expires header to msg {} from {}",command,accessor.getUser().getName());
String ttlString = accessor.getFirstNativeHeader("ttl");
long ttl = 30000;
try {
ttl = Long.parseLong(ttlString);
}
catch(Exception ex) {
log.error("TTL header received but not in correct format {}",ttlString);
}
accessor.addNativeHeader("expires", Long.toString(System.currentTimeMillis() + ttl));
return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
}
}
return message;
}
}
This is what i was looking for
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
The above code will get the actual StompHeaderAccessor of the message so if you manipulate the native headers they are directly reflected on the message while
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
will get a clone of the headers and you have to create a new message with the new cloned headers
full fixed code below
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
// StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
if(accessor != null) {
StompCommand command = accessor.getCommand();
if(command != null) {
log.debug("Receiving msg {} from {}",command,accessor.getUser().getName());
if(command == StompCommand.SEND) {
log.debug("Adding expires header to msg {} from {}",command,accessor.getUser().getName());
String ttlString = accessor.getFirstNativeHeader("ttl");
long ttl = 30000;
if(ttlString != null) {
try {
ttl = Long.parseLong(ttlString);
}
catch(Exception ex) {
log.error("TTL header received but not in correct format {}",ttlString);
}
}
accessor.addNativeHeader("expires", Long.toString(System.currentTimeMillis() + ttl));
// I don't need any more to create a new message
//return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
}
}
}
return message;
}
Since addNativeHeader succeeds, that indicates the message is still mutable - see addNativeHeader().
In any case, since the NATIVE_HEADERS message header is a MultiValueMap-valued header, you can update the header contents in-place.
Hence, there is no need to create a new message.
You would have to create a new message if you add a new header to the message itself (rather than updating the mutable contents of an existing header).
EDIT
I just ran a test; as long as the message is still mutable, you can change it...
#Test
public void test() {
Map<String, Object> map = new HashMap<String, Object>();
MutableMessageHeaders headers = new MutableMessageHeaders(map);
Message<String> message = MessageBuilder.createMessage("foo", headers);
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
accessor.addNativeHeader("foo", "bar");
System.out.println(message.getHeaders().get(NativeMessageHeaderAccessor.NATIVE_HEADERS));
accessor.setImmutable();
try {
accessor.addNativeHeader("baz", "qux");
fail("expected IllegalStateException");
}
catch (IllegalStateException e) {
}
}
That said, are you experiencing a performance problem or is this just a perceived issue. Message creation is not expensive.
I am looking for a way to get the key correspondent of the value with error. I can use foreach to get all errors in ModelState but how to associate it with your respective keys to get these names (keys names or index)?
You can iterate the ModelState (which is a ModelStateDictionary) and look at the keys, they correspond to the properties
Yishai's answer is not that straightforward for me, so I thought of leaving the following for others ending up here.
The following is a method I wrote to return all ModelStateDictionary errors, including the key where the error was found.
public static String GetErrorsJoined(this ModelStateDictionary modelStateDictionary, String separator = ". ")
{
String errorsJoined = String.Empty;
List<String> errors = new List<string>();
try
{
foreach(KeyValuePair<String, ModelState> keyValuePair in modelStateDictionary)
{
if (keyValuePair.Value.Errors.Count > 0)
{
Int32 keyDelimiterIndex = keyValuePair.Key.IndexOf('.');
String realKey = (keyDelimiterIndex > -1 && keyDelimiterIndex + 1 <= keyValuePair.Key.Length - 1) ? keyValuePair.Key.Substring(keyDelimiterIndex + 1) : keyValuePair.Key;
IEnumerable<String> keyErrors = keyValuePair.Value.Errors.Select(error => String.Format("{0}{1}{2}", error.ErrorMessage, separator, error.Exception.Message));
errors.Add(String.Format("Field [{0}]: {1}", realKey, String.Join(separator, keyErrors)));
}
}
errorsJoined = String.Join(separator, errors);
}
catch (Exception ex)
{
//do something
}
return errorsJoined;
}
I have an SIU S12 message that does not contain a PV2 segment. However, when I get the parsed message from NHAPI, the parent group for PV2, the SIU_S12_PATIENT group, return 1 for currentReps ("PV2"), which means the PV2 is present.
var parser = new NHapi.Base.Parser.PipeParser();
var parsedMessage = parser.Parse(message) as NHapi.Model.V231.Message.SIU_S12;
var patientGroup=parsedMessage.GetPATIENT(0);
// This call should not create the segment if it does not exist
int pv2Count=patientGroup.currentReps("PV2");
//pv2Count is 1 here despite no PV2 segment exists in the message
//also Both GetAll("PV2") and SegmentFinder say the PV2 segment is present
//DG1RepetitionsUsed is also 1 despite no DG1 segment is present in the message
I am trying to avoid writing code to evaluate every field in the segment. PV2 is just an example - there are a lot more segments that could be missing from the message source.
I am using NHAPI v 2.4, the latest version.
Update: following Tyson's suggestion I come up with this method;
var parser = new NHapi.Base.Parser.PipeParser();
var parsedMessage = parser.Parse(message) as NHapi.Model.V231.Message.SIU_S12;
var encodingChars = new NHapi.Base.Parser.EncodingCharacters('|', null);
var patientGroup = parsedMessage.GetPATIENT(0);
var dg1 = (NHapi.Model.V231.Segment.DG1) (patientGroup.GetStructure("DG1"));
string encodedDg1 = NHapi.Base.Parser.PipeParser.Encode(dg1, encodingChars);
bool dg1Exists = string.Compare(encodedDg1,
"DG1", StringComparison.InvariantCultureIgnoreCase)==0;
easiest thing that I have found to do is to determine if a segment is in a message is to search the actual string of the message for the segment name plus a pipe. So, for example
if(message.Contains("PV2|"))
{
//do something neat
}
From my experience, it is either that, or examining every sub-field under the segment to see if there is a value.
EDIT
I found another way to check that might work a little better. The PipeParser class has a couple of static methods on it that takes in ISegment, IGroup, and IType objects that will return a string representation of the object NHapi reference.
Sample code:
string validTestMessages =
"MSH|^~\\&|ADT1|MCM|LABADT|MCM|198808181126|SECURITY|ADT^A01|MSG00001|P|2.6\r" +
"EVN|A01-|198808181123\r" +
"PID|||PID1234^5^M11^HBOC^CPI^HV||JONES^WILLIAM^A^III||19610615000000|M||2106-3|1200 N ELM STREET^^GREENSBORO^NC^27401-1020|GL||||S||S|123456789|9-87654^NC\r" +
"PV1|1|I|||||TEST^TEST^TEST||||||||||||||||||||||||||||||||||||||||||||\r";
var encodingChars = new EncodingCharacters('|', null);
PipeParser parser = new PipeParser();
var message = parser.Parse(validTestMessages);
PV1 pv1 = (PV1)message.GetStructure("PV1");
var doctor = pv1.GetAttendingDoctor(0);
string encodedMessage = PipeParser.Encode(pv1, encodingChars);
Console.WriteLine(encodedMessage);
encodedMessage = PipeParser.Encode(doctor, encodingChars);
Console.WriteLine(encodedMessage);
Output:
PV1|1|I|||||TEST^TEST^TEST
TEST^TEST^TEST
if there is no segment or the item is empty, then the PiperParser will return an empty string.
You can read segment line by line to a file and add in hl7 Record object and check segment exist or not.
package com.sachan.ranvijay#gmail.com.hl7.msg;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import org.nule.lighthl7lib.hl7.Hl7Record;
import org.nule.lighthl7lib.hl7.Hl7Segment;
import com.stpl.hl7.dto.HL7PatientInfoDTO;
/**
* This class will parse the hl7 message. it can accept message file in the format of java.io.file
* as well as String. Its Uses org.nule.lighthl7lib.hl7.Hl7Record
* as a main component.
* #author Ranvijay.Singh
*
*/
public class PrepareHL7Message {
StringBuilder hl7Msg = new StringBuilder();
Hl7Record record = null;
public PrepareHL7Message(File file) throws Exception {
BufferedReader reader = new BufferedReader(
new FileReader(file));
String str = reader.readLine();
while (str != null) {
hl7Msg.append(str).append("\r");
str = reader.readLine();
}
reader.close();
try{
record = new Hl7Record(hl7Msg.toString());
}catch (Exception e) {
throw e;
}
}
public PrepareHL7Message(String msg) throws Exception {
try{
record = new Hl7Record(msg);
}catch (Exception e) {
throw e;
}
}
private HL7PatientInfoDTO getPatientOrderingPhysician(HL7PatientInfoDTO padto) {
Hl7Segment seg = record.getSegment("PV1");
if(seg!=null)
padto.setOrderingPhysician(seg.field(7).toString());
return padto;
}
}
//DTO.............
package com.sachan.ranvijay#gmail.com.hl7.dto;
public class HL7PatientInfoDTO {
/**
* maped with PV1-7
*/
private String orderingPhysician;
/**
* #return the orderingPhysician
*/
public String getOrderingPhysician() {
return orderingPhysician;
}
/**
* #param orderingPhysician the orderingPhysician to set
*/
public void setOrderingPhysician(String orderingPhysician) {
this.orderingPhysician = orderingPhysician;
}
}
The scenario of the problem is this
1) We map the struts field values to the dtos. The dtos contain integer fields which again are displayed on the screen.
2) Now I enter an incorrect value which gives conversion error for that integer field.
3) At that point in time I decide to quit the page(i.e press cancel), I get a conversion error. This is because the StrutsConversionErrorInterceptor gets called everytime.
Is there any way that I can skip the strutsConversionErrorInterceptor when I am calling a particular method the way we can skip validation using excludeMethods
Use this code to override Struts's StrutsConversionErrorInterceptor...
public class MyConversionErrorInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override";
protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
ValueStack stack = invocation.getStack();
try {
stack.push(value);
return "'" + stack.findValue("top", String.class) + "'";
} finally {
stack.pop();
}
}
#Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext invocationContext = invocation.getInvocationContext();
Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();
HashMap<Object, Object> fakie = null;
BaseAction baseAction = (BaseAction) invocation.getAction();
String buttonName = baseAction.getButtonName();
for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
if (shouldAddError(propertyName, value)) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
if(buttonName.equalsIgnoreCas("Next")){
va.addFieldError(propertyName, message);
}
}
if (fakie == null) {
fakie = new HashMap<Object, Object>();
}
if(buttonName.equalsIgnoreCas("Next")){
fakie.put(propertyName, getOverrideExpr(invocation, value));
}
}
}
if (fakie != null) {
// if there were some errors, put the original (fake) values in
// place right before the result
stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
invocation.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
if (fakie != null) {
invocation.getStack().setExprOverrides(fakie);
}
}
});
}
return invocation.invoke();
}
protected boolean shouldAddError(String propertyName, Object value) {
if (value == null) {
return false;
}
if ("".equals(value)) {
return false;
}
if (value instanceof String[]) {
String[] array = (String[]) value;
if (array.length == 0) {
return false;
}
if (array.length > 1) {
return true;
}
String str = array[0];
if ("".equals(str)) {
return false;
}
}
return true;
}
}
You can specify you button names on which you want validation to fire. In above code I have used "Next" in code you can see
if(buttonName.equalsIgnoreCas("Next"))
Yes, you can skip calling the interceptor.
Just remove the interceptor definition from your action definition in struts.xml file.
i.e., remove <interceptor-ref name="conversionError"/>
Mainly this interceptor adds any error found in the ActionContext's conversionErrors map as a field error (provided that the action implements ValidationAware). In addition, any field that contains a validation error has its original value saved such that any subsequent requests for that value return the original value rather than the value in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to the user).
After you removed this interceptor, if the struts failed to map the field with parameter of the object(i.e., from string to int), it throws result input action error.
This seems to be a better method to handle this scenario - using Conversion Validator. Repopulating Field upon conversion Error section is something very useful:
http://struts.apache.org/2.0.14/docs/conversion-validator.html